Michael H. Goldwasser | 31 May 2009 07:27

Challenge supporting custom deepcopy with inheritance


I've been playing around recently with customizing the deepcopy
semantics within an inheritance hierarchy, and run across the
following hurdle.

Assume that class B inherits from class A, and that class A has
legitimately customized its deepcopy semantics (but in a way that is
not known to class B).  If we want a deepcopy of B to be defined so
that relevant state inherited from A is copied as would be done for
class A, and with B able to control how to deepcopy the extra state
that it introduces.  I cannot immediately find a general way to
properly implement the deepcopy of B.

To make the discussion tangible, I include an outrageously artificial
example in the code fragment below.  In this code, class A implements
__deepcopy__ to intentionally create a clone that has a reversed
version of a list instance.  The beginning of the main test
demonstrates that an instance of A can successfully be cloned with this
semantics.  But the attempt to deepcopy an instance of B fails (the
current failure is because B has not implemented __deepcopy__ and the
inherited version of A.__deepcopy__ assumes the wrong constructor
signature).

The question is how class B should defined so that its instances can
"inherit" this deepcopy semantic.  Ideally, I'd like to find a recipe
for class B that works regardless of how A accomplishes its semantics.
That is, I can find ways to rewrite A to be more supportive of B's
goal, but I'd like to know if there is a general solution that could
be used if the author of B doesn't have control over A's
implementation.
(Continue reading)

Michael H. Goldwasser | 1 Jun 2009 05:28

Re: [Tutor] Challenge supporting custom deepcopy with inheritance


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).

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.

class B(A):
    def __init__(self):
        self.__registry = []

(Continue reading)

Gabriel Genellina | 1 Jun 2009 11:01
Picon
Favicon
Gravatar

Re: [Tutor] Challenge supporting custom deepcopy with inheritance

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={}):
(Continue reading)


Gmane