How to Copy Objects in Python?

Photo by Kevin Ku on Unsplash

How to Copy Objects in Python?

A brief Comparison of Shallow Copy VS Deep Copy

Python treats variables like reference variables in Java, i.e. the variable does not contain the object, it's more like a label with the name attached to the object.

Variables are not boxes holding the values, more like sticky notes on the object to reference it. Image from book Fluent Python

In the next example, we modify the list referenced by “var_one”, by appending another item. When we print “var_two” we get the same list.

This means the “var_two” references the same list referenced by “var_one”.

var_one = [1, 2, 3]
var_two = var_one
var_one.append(4)
print(var_two)
# [1, 2, 3, 4]

Since the variables are just references to the object, How do we Copy it?

Shallow Copy VS Deep Copy

Shallow copies are the easiest to make, but they don't always solve our problem.

Shallow copies copy the references to the copied object, i.e. you don’t create new objects you just reference the existing embedded objects. Basically, we stick another label on the object.

This saves memory and causes no problems if all the items are Immutable, But if there are mutable items, this may lead to unpleasant surprises.

Here's a Python tutor visualisation of a simple example of shallow copy:

list_1 = [3, [66, 55, 44], (7, 8, 9)]
list_2 = list(list_1)

Python tutor Visualization

This means that making a shallow copy of a list using the constructor will only reference the existing objects in the original list.

list_1.append(100)
list_1[1].remove(55)
print('list_1:', list_1)
# list_1: [3, [66, 44], (7, 8, 9), 100]
print('list_2:', list_2)
# list_2: [3, [66, 44], (7, 8, 9)]
list_2[1] += [33, 22]
list_2[2] += (10, 11)
print('list_1:', list_1)
# list_1: [3, [66, 44, 33, 22], (7, 8, 9), 100]
print('list_2:', list_2)
# list_2: [3, [66, 44, 33, 22], (7, 8, 9, 10, 11)]

And here's the last state of execution of the code above:

last state of execution of the code above

As you have seen, Shallow Copy can serve certain use cases but not all of them, and with that, we need to keep in mind that:

  • Any operations on embedded objects will be visible to other variables referencing the said objects unless that object is Immutable.

  • Tuples are Immutable, so any operation on them creates a new tuple and rebinds it to the variable like in the example.

Deep Copy to the rescue

Deep copies are duplicates that do not share references of embedded objects, i.e. if you deep copy a list or any object you will create new references for its embedded objects.

Python Standard Library has a module that implements this, it offers two functions copy() and deepcopy(), the first for the shallow copy and the latter for the deep copy.

import copy

class Bus:
    def __init__(self, passengers=None):
        if passengers is None:
            self.passengers = []
        else:
            self.passengers = list(passengers)

    def pick(self, name):
        self.passengers.append(name)

    def drop(self, name):
        self.passengers.remove(name)

bus1 = Bus(['Alice', 'Bill', 'Claire', 'David'])
bus2 = copy.copy(bus1)
bus3 = copy.deepcopy(bus1)

print(id(bus1), id(bus2), id(bus3))
# (4301498296, 4301499416, 4301499752)

bus1.drop('Bill')

print(bus2.passengers)
# ['Alice', 'Claire', 'David']

print(id(bus1.passengers), id(bus2.passengers), id(bus3.passengers))
# (4302658568, 4302658568, 4302657800)

print(bus3.passengers)
# ['Alice', 'Bill', 'Claire', 'David']

Sometimes making a deep copy can lead to bugs, some objects could have cyclic references or objects that may refer to external resources that should not be copied.

The solution is to implement your __copy__ and __deepcopy__ Special methods.

Conclusion

Python handles objects and mutability a little differently from statically typed languages like C# especially when it comes to Garbage Collection, and what we've introduced here is the tip of the iceberg, you will find here some references that could help your research.

Further Reading

Did you find this article valuable?

Support Algoryst's Corner by becoming a sponsor. Any amount is appreciated!