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. Here are some initial thoughts: * I'm only concerned with single inheritance (thankfully) * If A had relied on use of __getstate__ and __setstate__ to affect the desired change, then the inheritance works as desired. But I have an application where I want to leave the default pickling behavior alone, and only to change what happens with clones. * If we did not use inheritance, but instead had a class B that had an A instance in its state, this is easy. We would call deepcopy on the A instance to get an acceptable copy of that instance for the new B. But in the example below, we insist on B being a subtype of A. * If A.__deepcopy__ had started dup = A(self.__aTag) rather than dup = self.__class__(self.__aTag) then the following perverse approach for B.__deepcopy__ succeeds: def __deepcopy__(self, memo={}): tempBTag = self.__bTag tempBList = self.__bList del self.__bTag # hide these attributes to avoid risk del self.__bList # that they affect A.__deepcopy__ dupA = A.__deepcopy__(self, memo) self.__bTag = tempBTag self.__bList = tempBList dup = B.__new__(B) memo[id(self)] = dup dup.__dict__ = dupA.__dict__ dup.__bTag = copy.deepcopy(self.__bTag, memo) dup.__bList = copy.deepcopy(self.__bList, memo) return dup but this again presumes that we have some control over the strategy employed by class A, it relies on the fact that A.__deepcopy__ succeeded when the first parameter was actually an instance of class B rather than class A, and it seems like a perverse strategy on the whole, even if it does succeed on this example. Also, there could be a flaw n that dupA may recursively have included a reference back to that instance, yet we'd need such a reference to be to dup not dupA in the end. I'm hoping that I've just missed a cleaner way to do this. I welcome any comments, discussion, or suggestions. With regard, Michael ----------------------------------------------------------- import copy class A(object): def __init__(self, aTag): self.__aTag = aTag self.__aList = [] def addA(self, val): self.__aList.append(val) def __repr__(self): return ( "aTag: " + self.__aTag + "\naList ("+str(id(self.__aList))+"): " + repr(self.__aList) ) def __deepcopy__(self, memo={}): dup = self.__class__(self.__aTag) memo[id(self)] = dup dup.__aList = copy.deepcopy(self.__aList, memo) dup.__aList.reverse() return dup class B(A): def __init__(self, aTag, bTag): A.__init__(self, aTag) self.__bTag = bTag self.__bList = [] def addB(self, val): self.__bList.append(val) def __repr__(self): return ( A.__repr__(self) + "\nbTag: " + self.__bTag + "\nbList ("+str(id(self.__bList))+"): " + repr(self.__bList) ) # How can B support a deepcopy that provides deepcopy of bTag and # bList while getting the semantics of A's deepcopy for # attributes defined by A? if __name__ == '__main__': print "Test of class A\n===============" foo = A('alice') foo.addA(1) foo.addA(2) bar = copy.deepcopy(foo) print "\nOriginal Foo:" print foo print "\nOriginal (deepcopy) Bar:" print bar bar.addA(0) print "\n(mutated) Bar:" print bar print "Original Foo:" print foo print "Test of class B\n===============" foo = B('alice', 'bob') foo.addA(1) foo.addA(2) foo.addB('hello') foo.addB('goodbye') bar = copy.deepcopy(foo) # crashes print "\nOriginal Foo:" print foo print "\nOriginal (deepcopy) Bar:" print bar bar.addA(0) bar.addB('adios') print "\n(mutated) Bar:" print bar print "Original Foo:" print foo ----------------------------------------------------------- -- http://mail.python.org/mailman/listinfo/python-list