Python: Implementation of shallow and deep copy constructors

Kudayar Pirimbaev picture Kudayar Pirimbaev · Mar 28, 2013 · Viewed 8.4k times · Source

It is in most of the situations easy to implement copy constructors (or overloaded assignment operator) in C++ since there is a concept of pointers. However, I'm quite confused about how to implement shallow and deep copy in Python.

I know that there are special commands in one of the libraries but they don't work on classes that you have written by yourself. So what are the common ways to implement?

P.S. Showing process on some basic data structures (linked list or tree) will be appreciated.

EDIT: Thanks, they worked, it was my mistake in syntax. I am very interested in overwriting these functions with __copy__() and __deep_copy()__. For example. how can I make a deep copy without knowing which type of information is in a data structure?

Answer

Martijn Pieters picture Martijn Pieters · Mar 28, 2013

The python copy module can reuse the pickle module interface for letting classes customize copy behaviour.

The default for instances of custom classes is to create a new, empty class, swap out the __class__ attribute, then for shallow copies, just update the __dict__ on the copy with the values from the original. A deep copy recurses over the __dict__ instead.

Otherwise, you specify a __getstate__() method to return internal state. This can be any structure that your class __setstate__() can accept again.

You can also specify the __copy__() and/or __deepcopy__() methods to control just copy behaviour. These methods are expected to do all the copying themselves, the __deepcopy__() method is passed a memo mapping to pass on to recursive deepcopy() calls.

An example could be:

from copy import deepcopy

class Foo(object):
    def __init__(self, bar):
        self.bar = bar
        self.spam = expression + that * generates - ham   # calculated

    def __copy__(self):
        # self.spam is to be ignored, it is calculated anew for the copy
        # create a new copy of ourselves *reusing* self.bar
        return type(self)(self.bar)

    def __deepcopy__(self, memo):
        # self.spam is to be ignored, it is calculated anew for the copy
        # create a new copy of ourselves with a deep copy of self.bar
        # pass on the memo mapping to recursive calls to copy.deepcopy
        return type(self)(deepcopy(self.bar, memo))

This example defines custom copy hooks to prevent self.spam being copied too, as a new instance will calculate it anew.