Dash
3 min readMar 18, 2022

--

Are you assigning objects in this fashion?

Well you are not alone my friend.

For a long time I was under the notion that in python y=c would simply create a “fresh” object y having data of object x. I’m sure there are still people out there who haven’t thought about this; recently in my organization we faced a significant downtime due to this very bug.

Betraying commonly held expectation, object y is not “fresh” copy of x it simply creates a new reference of object x (both pointing to the same memory address). Well now you can see why this particularly could be dangerous in a live application. What then? surely there has to be a workaround? well of-course introducing to the concept of Shallow copy — the key to all of our problems.

Shallow copy

We will be using the copy function in python to simulate Shallow copy.

Lets understand this using an example in python,

RobsCart = ['eggs', 'bacon', 'cheese']
JennysCart = RobsCart
print(f"""JennysCart has -- {", ".join(JennysCart)}.""")
print(f"""RobsCart has -- {", ".join(RobsCart)}.""")
# ----------Will output----------
# JennysCart has -- eggs, bacon, cheese.
# RobsCart has -- eggs, bacon, cheese.
# Jenny swaps eggs for bread
JennysCart[0] = "bread"
print(f"""JennysCart has -- {", ".join(JennysCart)}.""")
print(f"""RobsCart has -- {", ".join(RobsCart)}.""")
# ----------Will output----------
# JennysCart has -- bread, bacon, cheese.
# RobsCart has -- bread, bacon, cheese.

Woah hold on I thought jenny swapped eggs for bread, how did bread sneak into Robs cart. OK I have made my point, using Shallow copy we can create a separate instance of JennysCart.

import copy RobsCart = ['egg', 'bacon', 'cheese']
JennysCart = copy.copy(RobsCart)
print(f"""JennysCart has -- {JennysCart}""")
print(f"""RobsCart has -- {RobsCart}""")
# ----------Will output----------
# JennysCart has -- ['egg', 'bacon', 'cheese']
# RobsCart has -- ['egg', 'bacon', 'cheese']
# Jenny swaps eggs for bread
JennysCart[0] = "bread"
print(f"""JennysCart has -- {JennysCart}""")
print(f"""RobsCart has -- {RobsCart}""")
# ----------Will output----------
# JennysCart has -- ['bread', 'bacon', 'cheese']
# RobsCart has -- ['egg', 'bacon', 'cheese']

That’s better, but what if we visualize Jenny’s eggs at a deeper level, this is starting to sound weird — This time instead of swapping the whole set of eggs for bread Jenny decides to swap one egg for another.

import copyRobsCart = [['egg1', 'egg2'], 'bacon', 'cheese']
JennysCart = copy.copy(RobsCart)
print(f"""JennysCart has -- {JennysCart}""")
print(f"""RobsCart has -- {RobsCart}""")
# ----------Will output----------
# JennysCart has -- [['egg1', 'egg2'], 'bacon', 'cheese']
# RobsCart has -- [['egg1', 'egg2'], 'bacon', 'cheese']
# Jenny swaps eggs for bread
JennysCart[0][1] = "egg3"
print(f"""JennysCart has -- {JennysCart}""")
print(f"""RobsCart has -- {RobsCart}""")
# ----------Will output----------
# JennysCart has -- [['egg1', 'egg3'], 'bacon', 'cheese']
# RobsCart has -- [['egg1', 'egg3'], 'bacon', 'cheese']

Yup so that definitely doesn’t work, OK remember when I said shallow copy will solve all of our problems — I lied!

Shallow copy only creates a new object and copies over the parents element, if the parent has a nested object shallow copy does not create an object for it.

Deep copy

To create “fresh” objects for the nested child as well we will have to use deepcopy function. Continuing our previous example where shallow copy failed our expectations.

import copyRobsCart = [['egg1', 'egg2'], 'bacon', 'cheese']
JennysCart = copy.deepcopy(RobsCart) ## Changing this to deepcopy
print(f"""JennysCart has -- {JennysCart}""")
print(f"""RobsCart has -- {RobsCart}""")
# ----------Will output----------
# JennysCart has -- [['egg1', 'egg2'], 'bacon', 'cheese']
# RobsCart has -- [['egg1', 'egg2'], 'bacon', 'cheese']
# Jenny swaps eggs for bread
JennysCart[0][1] = "egg3"
print(f"""JennysCart has -- {JennysCart}""")
print(f"""RobsCart has -- {RobsCart}""")
# ----------Will output----------
JennysCart has -- [['egg1', 'egg3'], 'bacon', 'cheese']
RobsCart has -- [['egg1', 'egg2'], 'bacon', 'cheese']

Deepcopy evaluates the object in a recursive manner, so it will create keep creating copies of nested objects until no more objects are left to be created.

Should I use deep copy or shallow copy?

In short, depends on your use-case. A general rule of thumb can be if you are planning to use nested objects, you will be better off with just replicating the object using deep copy, otherwise shallow copy should be sufficient in my opinion.

— Godspeed

--

--