In Python, assignments do not create new copies of assigned objects; they merely pass around references. This is ideal most of the time, but sometimes the developer desires a copy of some mutable object so they can make changes to it without modifying the original. This is where the copy library's deepcopy function shines.

>>> import copy

>>> a = list(range(10))
>>> deepcopy_of_a = copy.deepcopy(a)
>>> id(a) == id(deepcopy_of_a)
False

I'd been using deepcopy this way for years without realizing that it's possible to partially clone an object instead of cloning the entire object! It turns out that deepcopy accepts a second parameter, a memo, to store a mapping from ids to the objects that the ids should reference. When an empty memo dictionary is passed in, deepcopy populates it with any objects it encounters during the course of cloning:

>>> import copy

>>> memo = {}

>>> a = list(range(10))
>>> id(a)
139838084883392

>>> deepcopy_of_a = copy.deepcopy(a, memo)
>>> id(deepcopy_of_a)
139838083077696

>>> memo
{139838084883392: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], 139838083077184: [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]]}

Any objects that shouldn't be cloned can be inserted into the memo before deepcopy is called:

>>> import copy

>>> a = [[0], [1]]
>>> memo = {id(a[0]): a[0]}
>>> deepcopy_of_a = copy.deepcopy(a, memo)

>>> id(deepcopy_of_a[0]) == id(a[0])
True
>>> id(deepcopy_of_a[1]) == id(a[1])
False

And that's it! A magical parameter that provides a happy medium between the shallow copy and complete deepcopy functions!