copy module

One has to be careful when wanting to copy a variable/objects in Python as simple assigment doesn’t copy an object but just a reference. This problem is solver by the copy module. For simple lists and dictionaries use copy.copy, for class instances one must use copy.deepcopy to actually copy an object. Following is example that illustrates this:

import copy
class Something():
    a = 1
    def __init__(self):        
        self.b = 1
        self._value = 1
        self.list = [1,1,1]

    @property
    def value(self):
        return self._value
    
    @value.setter
    def value(self, value):
        self._value = value

Let’s just assign a new variable p=s, this will not be a copy but just a refernece to the same object:

s = Something()
p = s
id(p) == id(s)
True

If we now change p, s will change too:

def change_p_and_print(p, s):
    print('s is initially:', s.a, s.b, s.value, s.list)
    p.a = 10
    p.b = 10
    p.value = 10
    p.list.append(10)
    print('p is:          ', p.a, p.b, p.value, p.list)
    print('s is now:      ', s.a, s.b, s.value, s.list)
    print('s id:', id(s))
    print('p id:', id(p))
change_p_and_print(p, s)
s is initially: 1 1 1 [1, 1, 1]
p is:           10 10 10 [1, 1, 1, 10]
s is now:       10 10 10 [1, 1, 1, 10]
s id: 4352964496
p id: 4352964496

Let’s now make a copy (aka shallow copy):

s = Something()
p = copy.copy(s)
change_p_and_print(p, s)
s is initially: 1 1 1 [1, 1, 1]
p is:           10 10 10 [1, 1, 1, 10]
s is now:       1 1 1 [1, 1, 1, 10]
s id: 4352951920
p id: 4352964352

So we see that non-list variables are now copied, but list variable is still just referenced, and can be changed. As this website puts it: “a shallow copy doesn’t create a copy of nested objects, instead it just copies the reference of nested objects. This means, a copy process does not recurse or create copies of nested objects itself.”. id-s are now different.

To make a proper (i.e. deep copy) we use deep copy.

s = Something()
print(s.a, s.b, s.value, s.list)
p = copy.deepcopy(s)
change_p_and_print(p, s)
1 1 1 [1, 1, 1]
s is initially: 1 1 1 [1, 1, 1]
p is:           10 10 10 [1, 1, 1, 10]
s is now:       1 1 1 [1, 1, 1]
s id: 4352956480
p id: 4421994192