marius
marius

Reputation: 1442

In Python, how can I call copy.deepcopy in my implementation of __deepcopy__()?

I'd like to create a class which can provide a list of attributes which are not deepcopied by copy.deepcopy(). For example like this:

class CustomDeepcopy(object):

    a = SomeSimpleObject()
    b = SomeBigObject()

    def dont_deepcopy(self):
        return ['b']

    def __deepcopy__(self,memo):
        #Somehow call copy.deepcopy(self) and have it  
        #deepcopy self.a but not self.b
        #
        #For example, this *almost* works, 
        for attr in self.dont_deepcopy():
            val = getattr(self,attr,None)
            if val is not None:
                 memo[id(val)]=val
        return copy.deepcopy(self,memo)

The problem is that I don't think I can call copy.deepcopy() from within __deepcopy__() because that leads to an infinite recursion (since copy.deepcopy() first checks if my object has a __deepcopy__() method). Is there any way I can do this?

Upvotes: 4

Views: 1948

Answers (2)

l k
l k

Reputation: 157

copy.deepcopy will only call __deepcopy__ if the method exists - we can avoid this by saving the value of __deepcopy__, calling copy.deepcopy(...), and then restoring the value of __deepcopy__ before returning the result:

class CustomDeepcopy(object):

    a = SomeSimpleObject()
    b = SomeBigObject()

    def dont_deepcopy(self):
        return ['b']

    def __deepcopy__(self,memo):
        for attr in self.dont_deepcopy():
            val = getattr(self,attr,None)
            if val is not None:
                 memo[id(val)]=val
        deepcopy_method = self.__deepcopy__
        self.__deepcopy__ = None
        result = copy.deepcopy(self,memo)
        self.__deepcopy__ = deepcopy_method
        return result

Upvotes: 2

Jeff Tratner
Jeff Tratner

Reputation: 17096

Any time you implement a special method (like __getattr__, __deepcopy__, __str__, etc.) You either need to go up the mro with super or use some subset of the original.

I'm not totally clear how you're memoizing attributes, but I'm going to simplify your example. Let's say you always use the same a (and that it's immutable and doesn't need to be copied), but otherwise, you want to copy b. (and you can pass a and b directly to the constructor to make a new object.

class CustomDeepcopy(object):
    def __init__(self, a=None, b=None):
        if a:
            self.a = a
        if b:
            self.b = b

    a = SomeSimpleObject()
    b = SomeBigObject()

    @property
    def dont_deepcopy(self):
        return ['b']
    @property
    def deepcopy_attributes(self):
        return ['a']

    def __deepcopy__(self,memo):
        new_kwargs = dict((k, getattr(self, attr, None)) for attr in self.dont_deepcopy)
        for attr in self.deepcopy_attributes:
            new_kwargs[attr] = copy.deepcopy(getattr(self, attr, None))
        return self.__class__(**new_kwargs)

Upvotes: 1

Related Questions