[Tutor] Challenge supporting custom deepcopy with inheritance
Gabriel Genellina
gagsl-py2 at yahoo.com.ar
Mon Jun 1 05:01:40 EDT 2009
En Mon, 01 Jun 2009 00:28:12 -0300, Michael H. Goldwasser
<goldwamh at slu.edu> escribió:
> Hi Kent,
>
> Thanks for your thoughts.
>
> For the original example code, you are correct that we could make it
> work by having B provide the one-arg constructor to match A's
> constructor signature. But I don't see this as a general solution.
>
> Having B.__deepcopy__() make a call to A.__deepcopy__() assumes that A
> supports __deepcopy__ either direclty or indirectly. This is not
> guaranteed as __deepcopy__ is not supported all the way back to the
> object base class. It could be that the author of A has guaranteed
> that the syntax deepcopy(instanceA) is properly supported, but that
> could be done by other means without an explicit __deepcopy__ (such as
> relying on a true deep copy, or using getstate/setstate to affect both
> pickling and deepcopy).
In general, you have to know whether A implements __deepcopy__ or not.
This is a small price for the flexibility (or anarchy) in the copy/pickle
interfases: there are several ways to implement the same thing, and you
have to know which one your base class has chosen in order to extend it.
The following is a possible implementation that doesn't use __init__ at
all, so their different signature is not an issue:
# class A:
def __deepcopy__(self, memo={}):
dup = type(self).__new__(type(self))
dup.__aTag = self.__aTag
dup.__aList = copy.deepcopy(self.__aList, memo)
dup.__aList.reverse()
return dup
# class B:
def __deepcopy__(self, memo={}):
dup = A.__deepcopy__(self, memo)
dup.__bTag = self.__bTag
dup.__bList = copy.deepcopy(self.__bList, memo)
return dup
Note that A.__deepcopy__ does two things: a) create a new, empty,
instance; and b) transfer state. B.__deepcopy__ handles *its* portion of
state only. This can be written in a more generic way, relying on
__getstate__/__setstate__ (the methods that should return the current
state of the object):
# class A:
def __deepcopy__(self, memo={}):
dup = type(self).__new__(type(self))
if hasattr(self, '__getstate__'): state = self.__getstate__()
else: state = self.__dict__
state = copy.deepcopy(state, memo)
if hasattr(dup, '__setstate__'): dup.__setstate__(state)
else: dup.__dict__.update(state)
dup.__aList.reverse()
return dup
# remove __deepcopy__ definition from class B
Now, B (and any other subclass) is concerned only with __getstate__ /
__setstate__, and only when the default implementation isn't appropriate.
> As another basic puzzle, consider a class definition for B where B has
> a registry list that it doesn't want cloned for the new instance (but
> it does want pickled when serialized). This would seem to require
> that B implement its own __deepcopy__. We want to somehow rely on
> the class definition for A to enact the cloning fo the state defined
> by A. But without knowing about how A supports the deepcopy
> semantics, I don't see how to accomplish this goal.
I don't understand such bizarre requirement, but anyway, you can override
__deepcopy__ (make B fix only the part that the default implementation
doesn't implement well)
# A.__deepcopy__ as above
# class B:
def __deepcopy__(self, memo={}):
dup = A.__deepcopy__(self, memo)
dup.__registry = self.__registry
return dup
This [the need to know how certain feature is implemented in the base
class] is not special or peculiar to pickle/copy, although the multiple
(and confusing) ways in which a class can implement pickling doesn't help
to understand the issue very well.
Consider the + operator: when some subclass wants to implement addition,
it must know which of the several special methods involved (__add__,
__iadd__, __radd__) are implemented in the base class, in order to
extend/override them. Same for __cmp__/__eq__/__hash__: you can't just
ignore what your base class implements. All of this applies to other
languages too, but in Python, there is an additional consideration: the
mere *existence* of some methods/attributes can have consequences on how
the object behaves. In short, you can't blindly write __special__ methods.
--
Gabriel Genellina
More information about the Python-list
mailing list