Loading video player…

Making Shallow Copies in Python

00:00 As your introduction to copying, you’ll start by working with shallow copies. Shallow copying creates new objects but doesn’t duplicate the contents of composite objects.

00:09 Instead, it copies references to them. Objects created by shallow copying are new objects distinct from the original. However, they will share references to contained or nested objects, and that’s not a big deal when those objects are immutable.

00:24 But if not, this can cause side effects if those shared objects are mutated. In other words, operations on the copied object can have an effect on the original.

00:34 Let’s examine how all this works in the REPL.

00:38 There are many ways to create shallow copies in Python, but for this lesson you’ll work with the copy() function from the built-in copy module.

00:45 And for clarity, import it using the alias shallow_copy. from copy import copy as shallow_copy and to make displaying objects a little bit nicer, import the pp() function from the pprint module.

00:59 It’s a kind of pretty printer. from pprint import pp. And since we’re all grownups here, I trust that there’s nothing even remotely humorous about the name of this function.

01:11 Thank you. So what are you going to copy? To understand shallow copying, an mutable composite data type is perfect. In this case, a dictionary holding some inventory information for an imaginary grocery store.

01:25 inventory is a dictionary with two keys. The key fruits holds a dictionary with the key-value pairs, apple and the integer 50, and banana and the integer 30.

01:36 The key dairy also holds a dictionary with two key-value pairs: the key cheese and the value 15, and the key milk with the value 20.

01:48 Now create a backup of inventory using the shallow_copy function, backup = shallow_copy(inventory).

01:56 Use the pp() function to print both backup and inventory passing in the keyword argument width=50. Again, this is just for readability so you could also use the regular print() function. pp(backup, width=50) pp( inventory, width=50).

02:16 And they appear to be exactly the same. If you have doubts about the type of backup, you can use the built-in type() function just to confirm that it is a dict. type(backup), class 'dict', perfect. And now you can even use the equality operator to compare if the two dictionaries are in fact equivalent.

02:36 backup == inventory evaluates to True. And the final test, are they different objects? Use the is operator to see. backup is inventory returns False. Perfect. backup is a new separate object from inventory.

02:54 You can further prove this by mutating inventory. Add a new category seafood. inventory["seafood"] = a dictionary holding the key-value pairs of shrimp: 12, salmon: 10 and tuna: 8.

03:15 Pretty print inventory and backup setting depth=1. This will hide the contents of the nested dictionaries, so it’s a bit easier to read.

03:23 pp(inventory, depth=1) pp(backup, depth=1).

03:32 And you can see inventory has the new key of seafood, but backup is not. So far, they seem to be very separate objects.

03:40 But here’s where things get tricky. What if you modify one of the nested dictionaries inside inventory? Create a new key in inventory’s fruits dictionary called orange and set its value to 40. inventory ["fruits"]["orange"] = 40.

03:58 Examine inventory["fruits"]

04:02 and you should see the keys, apple, banana, and orange. Then examine backup["fruits"],

04:10 and you will also see the keys, apple, banana, and orange. What happened? Modifying the fruits dict within inventory also modified the fruits dict within backup.

04:22 Why? Because shallow copy only copies references to nested objects and not the values those references point to. You’ve already used the is operator to confirm that backup and inventory are distinct objects.

04:36 But what about the objects inside them? Compare the fruits dictionaries. backup["fruits"] is inventory["fruits"].

04:47 This returns True, meaning they are the same object. Any changes will affect both. Comparing with the id() function yields the same result.

04:56 id(backup["fruits"]) == id (inventory["fruits"]) also returns True. So while shallow copying does create a new object, it has clear limitations.

05:12 What if you want to create a complete copy of an object and all of its contents? Up next, deep copying.

Become a Member to join the conversation.