Loading video player…

Copying a List

Resource linked in this lesson: How to Copy Objects in Python: Shallow vs Deep Copy Explained

00:00 In this lesson, you’ll explore three concepts related to copying lists: aliasing, when you assign an existing list to a new value; shallow copying, which copies the references of objects contained in a list; and deep copying, which creates a new list, but also copies the elements of the original list.

00:19 You can create an alias of an existing variable using assignment. This can be done with any variable, not just lists. The syntax is simply new_variable = original_variable, but this isn’t truly a copying operation.

00:34 Instead, you’re binding a new name to the same underlying object. And this can be tricky when the underlying object is mutable, like a list. And as a result, modifying an alias will end up modifying the original list and vice versa.

00:48 To create a proper copy of a list that is a new list object, you can either shallow copy or deep copy. What’s the difference? First, you need to understand that in Python, lists store references to the objects they contain.

01:02 You can think of a list as a list of addresses pointing to the locations of the objects within. A shallow copy of a list is a new list that contains references to the same objects in the original list. You can create a shallow copy by slicing a slice with a single colon omitting start, stop, and step arguments.

01:19 Or you can use the .copy() method of lists, or the copy() function from the built-in copy module. On the other hand, a deep copy of a list is a new list whose references point to copies of the objects in the original list, making them disconnected from the objects in the original.

01:36 This makes a big difference as you’ll soon see. You can make deep copies using the deepcopy() function from the copy module.

01:44 Okay, time to write some code.

01:47 You’ll need a list to start with, so how about countries this time?

01:52 countries = a list of the strings [“US”, “Canada”, “Hungary”,` and "Austria"]. Now create an alias of the countries called nations.

02:01 nations = countries. So how can you verify that countries and nations are both names for the same object? You can use the built-in id() function.

02:10 Each object in memory has a unique identity. So if id() returns the same value for each variable, you know they reference the same object and you can compare with the equality operator.

02:21 id(countries) == id(nations), and it returns True, meaning both variables share an underlying object. You can further show this by modifying countries.

02:31 Using assignment: set countries[0] to the string "USA", confirm that countries has changed. Yep, "USA" instead of "US".

02:43 Now look at nations and the first value of nations has been updated to "USA" as well. So aliasing isn’t really creating copies in any meaningful sense.

02:53 Instead, try a shallow copy. You can use one of the techniques mentioned before. Their output will be equivalent. So for this example, I’ll just use slicing: nations = countries[:] with a colon and no arguments.

03:06 Now look at nations. Same content as before and incidentally, slicing like this works because the default behavior of a slice is to start from the beginning of a list and go to the end, thus returning all the same elements.

03:20 But are the IDs the same?

03:23 id(countries) == id(nations) returns False, meaning you now have two distinct objects, but recall that this is a shallow copy, meaning the list elements should share identities.

03:34 You can test this by looking at the IDs of the first element of both lists: id(nations[0]) == id(countries[0]). And this returns True. So while the lists are distinct, the items within are not.

03:49 But now you can change the elements in one list via assignment and it won’t affect the other: countries[0] equals the string = "United States". Look at countries, see that it’s changed and look at nations and see it remains as before.

04:04 "USA". The lists themselves remain distinct and this works fine in many cases. Where you’ll want to use deep copy instead is with something like a list of lists.

04:15 First, I’ll clear the screen.

04:17 Go ahead and import both copy and deepcopy from the copy module: from copy import copy, deepcopy and create a nested list holding three lists of integers: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]. Call it matrix.

04:39 Use the copy() function to create a shallow copy of matrix called shallow, and the deepcopy() function to create a deep copy of matrix called deep.

04:48 You can confirm that all three are different objects by checking their IDs: id(matrix) id(shallow), id(deep).

04:58 The values you see here will differ from mine, but what matters is that they’re distinct. This shows they are separate objects. Now you compare the IDs of the first element in each version of your matrix: id(matrix[0]),

05:13 id(shallow[0])

05:17 and id(deep[0]).

05:20 Aha, while matrix and shallow both reference the same object, deep does not. As a final bit of evidence, let’s alter the first sublist of matrix using index assignment:

05:32 set matrix[0][0] to 100, matrix[0][1] to 200, and matrix[0][2] to 300.

05:42 Now examine matrix. That first list has been updated as expected. Take a look at shallow. Again, the first list has been updated because it is only a shallow copy, it shares its elements with matrix.

05:55 And now let’s look at deep. It remains unchanged. All thanks to deep copying. As you’ve seen, copying objects in Python can get complex very quickly.

06:06 If you’re interested in learning even more about the nuances of copy operations, head on over to How to Copy Objects in Python: Shallow vs Deep Copying Explained.

06:16 It’s a topic that’s especially crucial to understand when dealing with mutable objects like lists. So in this lesson, you saw a couple of ways that you can modify a list.

06:26 Next up, even more.

Become a Member to join the conversation.