Loading video player…

Diving Into Deep Copying

00:00 Unlike shallow copying, deep copying recursively copies objects, and this ensures full independence from the source. Deep copies are new objects distinct from the original.

00:11 They contain copies of nested objects, not just references. As a result, they have no shared references with the original object and can be modified with no risk of causing side effects.

00:24 Once again, we go to the REPL to see some examples.

00:28 The main way to create deep copies in Python is using the deepcopy function from the built-in copy module. from copy import deepcopy. Start with the same inventory dictionary from the previous lesson.

00:41 inventory again is a dictionary with two keys, fruits and dairy. fruits holds a dictionary with the key-value pairs apple: 50 and banana: 30. dairy holds a dictionary with the pairs cheese: 15 and milk: 20.

00:58 This time, create a backup using the deepcopy function: backup = deepcopy(inventory). Use equality to see that right now their contents are equal,

01:11 and use the is operator to see that the objects are different.

01:16 Equality is True and identity is False, which is just what you’re looking for in a copy. Next, jump straight to mutating one of the inner dictionaries.

01:25 Add the key orange to the fruits dictionary and set its value to 40: inventory ['fruits']['orange'] = 40.

01:35 Examine inventory at the key fruits

01:39 and you see the keys, apple, banana, and orange. And examine backup at the key fruits,

01:47 apple, banana, and no orange. Not only are the objects distinct, but so are the nested objects. For one more piece of evidence, compare the dairy dictionaries of inventory and backup using the is operator: inventory['dairy'] is backup['dairy'] returns False.

02:07 They have no connection to each other. At this point, you’re probably thinking, well then, shouldn’t I just use deepcopy for everything? The thing is, creating copies of all these objects isn’t free.

02:18 There’s extra overhead in terms of computation and memory usage. For this reason, if the descendants of the top-level object you’re copying are themselves immutable, a shallow copy is actually the preferred choice.

02:31 See this example: import copy from the copy module once again: import copy as shallow_copy. Create a list of two strings, milk and eggs, and call it cart.

02:45 cart = ['milk', 'eggs']. Create a shallow copy of cart called basket.

02:53 basket = shallow_copy(cart). As a shallow copy, the two lists are distinct objects, but is there a risk of side effects? Well, think about what kind of changes you can make to cart.

03:06 You could add an item, right? Try cart. append('bananas').

03:14 But what else can you do? Can you mutate any of the existing items? Well, strings are immutable, but they do support augmented assignment allowing you to add to an existing string.

03:27 So try this: cart[0] += ' chocolate' with a space at the beginning. Now look at cart. cart contains the strings milk chocolate, eggs, and bananas.

03:41 Look back at basket. Just like it began, basket only holds the strings milk and eggs. See? No side effects.

03:49 The append operation only affected the top-level object, cart, which was already distinct from basket. And the augmented addition of chocolate to milk didn’t actually mutate the underlying object, but instead replaced it with a new object.

04:04 And that is what took the first place position in the list cart, leaving basket and the original string as they were. So in a situation like this, deepcopy would’ve been overkill.

04:15 Alright, up to this point, you’ve done all your copying using the copy module. In the next lesson, we’ll explore the myriad other options Python offers for producing copies.

Become a Member to join the conversation.