Search code examples
python-3.xdeep-copyweak-references

python deepcopy not deepcopying user classes?


I will get straight to the example that made me ask such a question:

Python 3.6.6 (default, Jul 19 2018, 14:25:17) 
Type 'copyright', 'credits' or 'license' for more information
IPython 6.4.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: from copy import deepcopy

In [2]: class Container:
   ...:     def __init__(self, x):
   ...:         self.x = x
   ...:         

In [3]: anobj = Container("something")

In [4]: outobj = Container(anobj)

In [5]: copy = deepcopy(outobj)

In [6]: id(copy) == id(outobj)
Out[6]: False

In [7]: id(copy.x) == id(outobj.x)
Out[7]: False

In [8]: id(copy.x.x) == id(outobj.x.x)
Out[8]: True

As per the documentation of deepcopy, I was expecting the last line to have False as a response, i.e. that deepcopy would also clone the string.

  1. Why it is not the case?
  2. How can I obtain the desired behaviour? My original code has various levels of nesting custom classes with "final" attributes that are predefined types.

Thanks in advance


Solution

  • At least in CPython, the ID points to the object address in memory. Because Python strings are immutable, deepcopy won't create a different ID. There's really no need to create a different string in memory to hold the exact same data.

    The same happens for tuples that only hold immutable objects, for example:

    >>> from copy import deepcopy
    >>> a = (1, -1)
    >>> b = deepcopy(a)
    >>> id(a) == id(b)
    True
    

    If you tuple holds mutable objets, that won't happen:

    >>> a = (1, [])
    >>> b = deepcopy(a)
    >>> id(a) == id(b)
    False
    

    So in the end the answer is: deepcopy is working just fine for your classes, you just found a gotcha about copying immutable objects.