Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Default behavior of copy module on user-defined classes

When copy.copy or copy.deepcopy is called on an instance of a user-defined class that does not have a __copy__ or __deepcopy__ method, what does Python guarantee will happen? The official docs are disturbingly non-explicit on this matter. Will the function always just return a new instance of the same class with a shallow/deep copy of the original object's __dict__ (or whatever the equivalent is when __slots__ are involved)? Can the behavior differ between CPython, PyPy, etc.? Does the behavior differ between Python 2 and 3? (Ignore old-style classes.) What would make one need to define explicit __copy__/__deepcopy__ methods instead of using the default behavior?

References to explicit (better than implicit!) authoritative statements needed.

like image 904
jwodder Avatar asked Nov 27 '15 22:11

jwodder


1 Answers

From reading through the copy module's source, among other documents, I have determined the following:

  • When copy or deepcopy is called on an instance of a user-defined new-style class that does not have a __copy__ method and has not registered a callable with copy_reg.pickle, the instance's __reduce_ex__ method is called with a protocol of 2. object defines a __reduce_ex__ method that is inherited by all new-style classes that do not define their own, and so every instance has a __reduce_ex__.

  • __reduce_ex__ and __reduce__ return values usable for pickling, and the copy module uses these to emulate unpickling, creating & returning a new object made from the state of the original object. Moreover, when using deepcopy, the object's state (specifically, the third element of the tuple returned by __reduce_ex__/__reduce__) is deepcopied recursively before applying it to the new object.

  • Some basic testing shows that calling __reduce_ex__(2) on an instance x of a simple user-defined class returns (<function __newobj__>, (type(x),), x.__dict__, None, None). In both Python 2 and Python 3, if the class does not have a __setstate__ method, the copy module will then perform the equivalent of the following:

    callable, args, state, _, _ = x.__reduce_ex__(2)
    y = callable(*args)
    if deepcopying:
        state = deepcopy(state)
    y.__dict__.update(state)
    return y
    

So it appears that the default behavior of the copy functions on instances of user-defined classes is indeed to do the useful & simple thing and create a new object with a (possibly deep) copy of the original object's state.

like image 121
jwodder Avatar answered Oct 11 '22 15:10

jwodder