How-To: Copy a Dictionary or List in Python

My goals with this post are to show how to copy a dictionary or list in Python, the different ways to perform the copies, and the differences between them.

CONTENTS

  1. BACKGROUND
  2. COPYING EXAMPLES
    1. Example of Variable Assignment
    2. Shallow Copy Without Nested Objects Example
    3. Shallow Copy With Nested Objects Example
    4. Deep Copy Example

BACKGROUND

I am writing this post due to some issues I came across recently after copying dictionaries in Python. My issues occurred when I edited items in the copy of a dictionary. I expected the changes to only appear in the copy, however the changes also appeared in the original dictionary.

The short explanation of why this happened is that it was due to:

  • how dictionaries (and lists) are stored in Python
  • the fact that I only did a limited/shallow copy of the original dictionary
  • I made changes to nested dictionaries contained inside of the original dictionary, however, the copy operation I performed did not copy the nested dictionaries to the copied version of the original dictionary.

COPYING EXAMPLES

Please note: I will be showing examples with dictionaries, however, the same concepts apply to lists in Python.

Example of Variable Assignment

The general first assumption of how to copy a dictionary is to just set a new variable equal to a variable containing a dictionary. This actually does not work as one would expect.

Shallow copy of a dictionary where changes to one dictionary affect the other dictionary
Shallow copy of a dictionary, where changes effect both dictionaries

Notice that by changing dict_b, we also make the same change to dict_a. This happens because a variable containing a dictionary, list, or object in Python actually just contains a pointer to that dictionary, list, or object. Therefore, dict_a actually contains a pointer to the address of a dictionary object. So, when we set dict_b equal to dict_a, we are just assigning the same pointer to dict_b, so now dict_b also points to the same dictionary object.

# actual_dict is stored in memory address A1

dict_a = A1 -> actual_dict
dict_b = dict_a
dict_b = A1 -> actual_dict

To actually copy the dictionary, we want to perform an operation that will create a copy of what we call “actual_dict” above. Below, we illustrate a couple of ways to make copies of a dictionary.

Shallow Copy Without Nested Objects Example

We may use either dict.copy() or we may pass a dictionary as a parameter to the dict() function in order to perform a simple/shallow copy of a dictionary.

Shallow copy of a dictionary where changes to one dictionary affect the other dictionary
Shallow copy of a dictionary; changes to basic elements in one dictionary only effect that dictionary

Now we can see that changes we make to one dictionary do not appear in another dictionary. This holds true so long as there are no objects, such as dictionaries or lists, as values within our original dictionary before we copy it.

Shallow Copy With Nested Objects Example

Here is an example of doing a normal/shallow copy of a dictionary when it has nested objects, such as dictionaries or lists within it.

Shallow copy of a dictionary with a nested dictionary inside, where changes to the nested dictionary in one outer dictionary affect the other outer dictionary
Shallow copy of a dictionary with nested dictionary; changes to nested dictionary in one outer dictionary are also reflected in the other outer dictionary

Now we observe that when adding a value to the nested dictionary, the change is shown in both dict_a and dict_b. Adding a simple key/value pair in dict_b, however, only affects dict_b. This occurs because the value of key “2” is stored as a pointer to a dictionary object. So, when we do our shallow copy, just the pointer (address of the nested dictionary object) is copied. Therefore, when we make changes to it from either dict_a or dict_b, we are changing the same object.

# actual_dict is stored in memory address A1
# original_dict is stored in memory address B1
# copied_dict is stored in memory address C1

dict_a = B1 -> original_dict
dict_a["2"] = A1 -> actual_dict
dict_b = dict_a.copy()
dict_b = C1 -> copied_dict
dict_b["2"] = A1 -> actual_dict

If we want to be able to make a full copy of a dictionary, including all nested objects, then we need to import the copy module and use the deepcopy() method.

Deep Copy Example

Deep copy of a dictionary with a nested dictionary inside, where changes to the nested dictionary in one outer dictionary only apply to that outer dictionary
Deep copy of a dictionary with nested dictionary; changes to nested dictionary in one outer dictionary only apply to that dictionary and not the other outer dictionary

We can see above that after editing the nested dictionary within dict_b the changes are only reflected in dict_b and not dict_a.

# actual_dict is stored in memory address A1
# original_dict is stored in memory address B1
# copied_dict is stored in memory address C1
# a_copy_of_actual_dict is stored in memory address D1

dict_a = B1 -> original_dict
dict_a["2"] = A1 -> actual_dict
dict_b = dict_a.copy()
dict_b = C1 -> copied_dict
dict_b["2"] = D1 -> a_copy_of_actual_dict

Leave a Reply

Your email address will not be published. Required fields are marked *