This article explains how you can copy immutable objects in Python from one variable to another using shallow copy and deep copy approaches.

Python objects can be immutable (integers, float, bool, tuple, etc.), or mutable (lists, dictionaries, sets). Since immutable objects cannot be updated, copying them always copies their memory locations.

For mutable objects, you have three main ways of copying them: using an assignment operator, shallow copying an object or deep copying an object. You’ll study these three approaches in this tutorial.

Copying via Assignment Operator

Before we study the difference between shallow copying and deep copying Python objects, it’s important to understand how the assignment operator (=) assigns values to variables.

The assignment operator simply assigns the memory reference of an object into another instead of making new copies of the values stored by a variable. Let’s see an example.

The following script defines a list, num1, and assigns this list variable to nums2.

nums1 = [10, 20, 30, 40, 50]

nums2 = nums1

print(nums2)
print(nums1)

Output:

[10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]

If you look at the memory location of both the nums1 and nums2 variables, you will see that they point to identical memory location.

print(hex(id(nums1)))
print(hex(id(nums2)))

Output:

0x1560e31c7c0
0x1560e31c7c0

This means that when you update a value in any of the two list variables, the change will be reflected in the other list. For example, if you assign the value 500 to the second index of the nums2 list, you’ll notice that the second index of the nums1 list is also updated.

nums2[2] = 500
print(nums2)
print(nums1)

Output:

[10, 20, 500, 40, 50]
[10, 20, 500, 40, 50]

This is an important concept to understand and one you might not have realized.


Get Our Python Developer Kit for Free

I put together a Python Developer Kit with over 100 pre-built Python scripts covering data structures, Pandas, NumPy, Seaborn, machine learning, file processing, web scraping and a whole lot more - and I want you to have it for free. Enter your email address below and I'll send a copy your way.

Yes, I'll take a free Python Developer Kit

Making a Shallow Copy with copy() Function

If you want to copy the values of a variable, like list items, instead of their memory locations, you can make shallow copies of objects.

The copy() function can be used to make a shallow copy in Python. However, in case of nested lists, the copy() function only copies the top level list items. For the nested list items, the memory locations are copied. This is why this type of copy operation is called the shallow copy. Let’s show you what we mean.

Copying Flat Lists with copy() Function

As discussed, whe shallow copying flat lists (think 1-dimensional lists) using the copy() function, the values are copied instead of memory locations. Here’s an example where list nums1 is shallow copied to the list nums2.

nums1 = [10, 20, 30, 40, 50]

nums2 = nums1.copy()

print(nums2)
print(nums1)

Output:

[10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]

In this case, the nums2 list stores the items from the nums1 list in a new memory location. You can verify this by printing the memory locations of nums1 and nums2 lists. You’ll see that they’re different:

print(hex(id(nums1)))
print(hex(id(nums2)))

Output:

0x1560e31c200
0x1560e31c080

Now, if you update a list item in any of the two lists, that change will no logner be reflected in the other list.

nums2[1] = 500
print(nums2)
print(nums1)

Output:

[10, 500, 30, 40, 50]
[10, 20, 30, 40, 50]

Copying Nested Lists with Copy() Function

If you shallow copy the lists containing nested sub-lists using the copy() function, the top level list items will be shallow copied, but when you get to the nested list items, only their memory locations are copied. Let’s see an example. The following script defines the nums1 list with three nested lists. The nums1 list is copied to the nums2 list using the copy() function.

nums1 = [[1,2,3],[4,5,6],[7,8,9]]
nums2 = nums1.copy()

print(nums2)
print(nums1)

Output:

[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

If you print the memory locations of the top level lists, you’ll observe that the memory locations are different, as expected

print(hex(id(nums1)))
print(hex(id(nums2)))

Output:

0x1560e31e1c0
0x1560d306c40

As expected, if you update an item in the top-level list, the change is not reflected in the other list since the top level items values are copied.

nums2[1] = 500
print(nums2)
print(nums1)

Output:

[[1, 2, 3], 500, [7, 8, 9]]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

However, for nested list items, only the memory locations of the items are copied. Therefore, if you update a nested list item in one list, the change will be reflected in the other list, as shown in the following script.

nums2[0][1] = 500
print(nums2)
print(nums1)

Output:

[[1, 500, 3], 500, [7, 8, 9]]
[[1, 500, 3], [4, 5, 6], [7, 8, 9]]

You can verify this by printing the memory locations of the corresponding nested items in the two lists. For instance, the output of the script below shows that the memory location of the item at the second index of the first nested list is the same for both the nums1 and nums2 lists.

print(hex(id(nums2[0][1])))
print(hex(id(nums1[0][1])))

Output:

0x1560ee55030
0x1560ee55030

Get Our Python Developer Kit for Free

I put together a Python Developer Kit with over 100 pre-built Python scripts covering data structures, Pandas, NumPy, Seaborn, machine learning, file processing, web scraping and a whole lot more - and I want you to have it for free. Enter your email address below and I'll send a copy your way.

Yes, I'll take a free Python Developer Kit

Making a Deep Copy with deepcopy() Function

Fortunately, there is a way to copy nested items to brand new memory locations with Python. If you want to recursively copy the values of all the top level and nested items in a Python object, like a list, you can use the deepcopy() function.

Copying Flat Lists with deepcopy() Function

While copying a flat list, the deepcopy() functions behaves in the same way as the copy() function; all the items are copied instead of their memory locations.

Here’s an example:

import copy

nums1 = [10, 20, 30, 40, 50]

nums2 = copy.deepcopy(nums1)

print(nums2)
print(nums1)

Output:

[10, 20, 30, 40, 50]
[10, 20, 30, 40, 50]

You can print the memory locations of the nums1 and nums2 lists to see that the values are copied instead of memory locations.

print(hex(id(nums1)))
print(hex(id(nums2)))

Output:

0x1560d306c40
0x1560ee40ec0

Like the shallow copy, an update to the top level list item will not be reflected in the other list when using a deep copy.

nums2[1] = 500
print(nums2)
print(nums1)

Output:

[10, 500, 30, 40, 50]
[10, 20, 30, 40, 50]

Copying Nested Lists with deepcopy() Function

When using a deep copy, the top level and the nested list items are recursively copied to new memory locations. The following script copies nums1 list to nums2 using the deepcopy() function.

import copy
nums1 = [[1,2,3],[4,5,6],[7,8,9]]
nums2 = copy.deepcopy(nums1)

print(nums2)
print(nums1)

Output:

[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

You can see that the memory locations of the top level list items are different.

print(hex(id(nums1)))
print(hex(id(nums2)))

Output:

0x1560ee56300
0x1560ee44180

Therefore, a change in one of the top level list items is not reflected in the other list.

nums2[1] = 500
print(nums2)
print(nums1)

Output:

[[1, 2, 3], 500, [7, 8, 9]]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Furthermore, the values, rather than the memory locations, of the nested items are copied. Therefore, a change in a nested list item is not reflected in the other list. Look at the following example:

nums2[0][1] = 500
print(nums2)
print(nums1)

Output:

[[1, 500, 3], 500, [7, 8, 9]]
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

Finally, you can print the memory locations of the nested list items to see that they are indeed stored in different memory locations.

print(hex(id(nums2[0][1])))
print(hex(id(nums1[0][1])))

Output:

0x1560ee554f0
0x156080d6950

Conclusion

As you can see, there are multiple ways to copy objects in Python. The choice of the method depends on your personal objective. If you are only interested in copying memory locations, you can use the assignment operator (=). If you want to copy values instead of memory locations, the copy() or deepcopy() functions will be a better fit for you. Just remember that the copy() function only copies values of the top level items whereas the deepcopy() function copies values of the top level and all nested items.

Found this helpful? Subscribe using the form below for more Python tips like this one.


Get Our Python Developer Kit for Free

I put together a Python Developer Kit with over 100 pre-built Python scripts covering data structures, Pandas, NumPy, Seaborn, machine learning, file processing, web scraping and a whole lot more - and I want you to have it for free. Enter your email address below and I'll send a copy your way.

Yes, I'll take a free Python Developer Kit