Copying of objects, the copy module

In Python, mutable objects cannot be copied by assigning one variable to another, since in this case the reference to the object is copied, not the object itself. As a result, when an object changes through one variable, the changes are visible through another. Therefore, other methods are used for copying.

Lists, dictionaries, and some other built-in types have a copy() method that creates their shallow copies. In the case of a shallow copy, if the object is compound, that is, includes other mutable objects, then they are not copied, but only references to them are copied.

If a full copy of the object is required, use the copy module's deepcopy() function. In addition to this function, there is also a copy() function that performs a shallow copy, similar to the copy() methods of dictionaries and lists.

The difference between copy() and deepcopy() is illustrated by this example:

>>> import copy
>>> nums = [1, 2, 3]
>>> data = {'a': 10, 'b': nums}
>>> data
{'a': 10, 'b': [1, 2, 3]}
>>> data_copy = copy.copy(data)
>>> data_deep = copy.deepcopy(data)
>>> data_copy
{'a': 10, 'b': [1, 2, 3]}
>>> data_deep
{'a': 10, 'b': [1, 2, 3]}
>>> data_copy['a'] += 2
>>> nums[1:1] = [254]
>>> data
{'a': 10, 'b': [1, 254, 2, 3]}
>>> data_copy
{'a': 12, 'b': [1, 254, 2, 3]}
>>> data_deep
{'a': 10, 'b': [1, 2, 3]}

In the case of deepcopy(), a copy of the nested list was created, copy() does not do it.

The absence of a variable in the list does not change the situation at all:

>>> d = {1: [1,2], 2: 10}
>>> c = copy.copy(d)
>>> c
{1: [1, 2], 2: 10}
>>> c[1].append(3)
>>> c
{1: [1, 2, 3], 2: 10}
>>> d
{1: [1, 2, 3], 2: 10}

The is operator checks whether two variables refer to the same object, the == operator checks the equality of values.

>>> d = {1:2}
>>> c = d
>>> e = d.copy()
>>> d is c
True
>>> d is e
False
>>> d == e
True

Using the functions of the copy module, you can copy objects of your own classes:

import copy


class A:
    def __init__(self):
        self.lst = []


a = A()
a.lst.append(10)

b = copy.copy(a)
b.lst[0] = 20

print(a.lst, b.lst)
print(a is b)
print(a)
print(b)

The result:

[20] [20]
False
<__main__.A object at 0x7f9b5e2bda58>
<__main__.A object at 0x7f9b5e2caac8>

As we can see, despite the fact that the objects a and b are different, the lst field of both refers to the same list. In order to copy the list when copying an object, use the deepcopy() function.