i am implementing a python class that provides some nested data structure. i want to add support for copying through copy.copy()
and deep copying through copy.deepcopy()
, which, as the docs for the copy module describe, involves writing __copy__()
and __deepcopy__
special methods.
i know how to teach my class to make a copy of itself, but i want to avoid going through __init__()
on the new instance, since __init__()
does some things that my copying logic does not want (or need) to do.
what i ended up with is this method, which works as intended:
def __copy__(self):
cls = type(self)
obj = cls.__new__(cls)
# custom copying logic that populates obj goes here
return obj
my question is: is calling cls.__new__(cls)
the right approach for a __copy__()
implementation that wants to skip __init__()
for the copy? or is there a more "pythonic" approach that i overlooked?
To make a deep copy, use the deepcopy() function of the copy module. In a deep copy, copies are inserted instead of references to objects, so changing one does not change the other.
Deep copy is a process in which the copying process occurs recursively. It means first constructing a new collection object and then recursively populating it with copies of the child objects found in the original. In case of deep copy, a copy of object is copied in other object.
In Python, we use = operator to create a copy of an object. You may think that this creates a new object; it doesn't. It only creates a new variable that shares the reference of the original object.
Use the copy. deepcopy() Function to Deep Copy a List in Python. The deepcopy() function from the copy module is used to create a deep copy of the list specified. If we alter this copy, then the contents of the original list remain the same and are not changed.
I don't know if this is more pythonic, but you could use a flag.
from collections import Mapping
from copy import copy, deepcopy
class CustomDict(dict, Mapping):
_run_setup = True
def __init__(self, *args, **kwargs):
self._dict = dict(*args, **kwargs)
if args and isinstance(args[0], CustomDict):
self._run_setup = args[0]._run_setup
if self._run_setup:
print("Doing some setup stuff")
else:
try:
print("Avoiding some setup stuff")
finally:
self._run_setup = True
def __getitem__(self, key):
return self._dict[key]
def __iter__(self):
return iter(self._dict)
def __len__(self):
return len(self._dict)
def __copy__(self):
self._run_setup = False
try:
copied_custom_dict = CustomDict(self)
finally:
self._run_setup = True
return copied_custom_dict
In the __init__
above, the conditional setup stuff is only done if _run_setup = True
. The only way to avoid this is by calling CustomDict
with the first parameter being an instance of itself with _run_setup = False
. This way, it's easy to flip the setup switch in different methods.
The try...finally
blocks look clunky to me, but it's a way to make sure every method starts and ends with _run_setup = True
.
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