Object Cloning

Object Cloning

TL;DR

  • Making a shallow copy of an object won’t clone child objects. → The copy is not fully independent of the original.
  • A deep copy of an object will recursively clone child objects.The clone is fully independent of the original, but creating a deep copy is slower.
  • You can deep or shallow copy arbitrary objects with the standard copy module.

Shallow vs. Deep Copy

For compound objects, there’s an important difference between shallow and deep copying:

  • Shallow copy

    • Constructing a new collection object and then populating it with references to the child objects found in the original → only one level deep
    • The copying process does NOT recurse and therefore won’t create copies of the child objects themselves.
  • Deep copy

    • Makes the copying process recursive

      • first constructing a new collection object
      • then recursively populating it with copies of the child objects found in the original

      → Copying an object this way walks the whole object tree to create a fully independent clone of the original object and all of its children.

Shallow Copy

Python’s built-in mutable collections like lists, dicts, and sets can be shallowly copied by calling their factory functions on an existing collection:

new_list = list(original_list) 
new_dict = dict(original_dict) 
new_set = set(original_set)

Example

we’ll create a new nested list and then shallowly copy it with the list() factory function:

>>> xs = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 
>>> ys = list(xs) # Make a shallow copy

Now ys will be a new and independent object with the same contents as xs:

>>> xs
[[1, 2, 3], [4, 5, 6], [7, 8, 9]] 

>>> ys
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

To further verify that ys is really independent from the original, we add a new sublist ot the original xs and check to make sure this modification didn’t affect the copy ys:

>>> xs.append(['new'])
>>> xs
[[1, 2, 3], [4, 5, 6], [7, 8, 9], ['new']] 

>>> ys
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]

However, because we only created a shallow copy of the original list xs, ys still contains references to the original child objects stored in xs. These children were NOT copied. They were merely referenced again in the copied list.

>>> xs[1][0] = 'X'
>>> xs
[[1, 2, 3], ['X', 5, 6], [7, 8, 9], ['new']] 

>>> ys
[[1, 2, 3], ['X', 5, 6], [7, 8, 9]]

The diagram below shows how this works:

python_shallow_copy

Use Thecopy Module

The copy module in the Python standard library provides a simple interface for creating shallow and deep copies of arbitrary Python objects.

  • copy.deepcopy() creates deep copy
  • copy.copy() creates shallow copy
For built-in collections it’s considered more Pythonic to simply use the list, dict, and set factory functions to create shallow copies.