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.
- COPYING EXAMPLES
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.
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.
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.
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.
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
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