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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With