Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

correct way to add custom (deep) copying logic to a python class

Tags:

python

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?

like image 585
wouter bolsterlee Avatar asked May 28 '17 19:05

wouter bolsterlee


People also ask

How do you make a deep copy in Python?

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.

How does deep copy work in Python?

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.

How do you copy a class in Python?

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.

How do you deep copy an array in Python?

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.


1 Answers

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.

like image 71
Nathan Werth Avatar answered Oct 17 '22 04:10

Nathan Werth