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.