Problem using copy.copy with my own class
jeff_barish at earthlink.net
Thu Apr 24 18:17:35 CEST 2008
George Sakkis wrote:
> First off, inheriting from a basic builtin type such as int and
> changing its constructor's signature is not typical; you should
> rethink your design unless you know what you're doing.
Nah, I would never claim to know what I'm doing. However, I have to say
that I have been finding this technique very useful. When I started
developing this program, I used an int. Then I discovered that I needed to
have a string associated with the int. By subclassing int to add a string,
I managed to make the change transparent to the code I had already written.
Only the new code that needed the associated string knew that it was
available. In another case, I subclassed str so that I could have a long
form for a string (e.g., a full name attached to the surname). Are these
applications of subclassing bad form? What is the motivation for your
> One way to make this work is to define the special __copy__ method
> , specifying explicitly how to create a copy of a Test instance:
> Normally (i.e. for pure Python classes that don't
> subclass a builtin other than object) copy.copy() is smart enough to
> know how to create a copy without an explicit __copy__ method, so in
> general you don't have to define it for every class that has to be
Yes, I noted in my original posting (which seems to have fallen off this
thread) that the __copy__method solved the problem (at least on one
platform). However, I was wondering why it was necessary when what I was
defining was supposedly the default action. Thanks for your explanation.
> The traceback is not obvious indeed. It turns out it involves calling
> the arcane __reduce_ex__ special method  defined for int, which
> returns a tuple of 5 items; the second is the tuple
> (<class '__main__.Test'>, 0) and these are the arguments passed to
> Test.__new__. So another way of fixing it is keep Test.__new__
> compatible with int.__new__ by making optional all arguments after the
This suggestion is very interesting. It seems to be an alternative to the
solution suggested by Gabriel. The reference that Gabriel provided
includes the statement:
Instances of a new-style type C are created using
obj = C.__new__(C, *args)
where args is the result of calling __getnewargs__() on the original
object; if there is no __getnewargs__(), an empty tuple is assumed.
Gabriel's solution using __getnewargs__ assures that args receives a
non-empty tuple. Your solution renders the empty tuple impotent by
specifying default values. Correct me if I am wrong.
I would be interested in a translation into English of the following
statement from the same reference that Gabriel provided:
Implementing this method [i.e., __getnewargs__] is needed if the
type establishes some internal invariants when the instance is
created, or if the memory allocation is affected by the values
passed to the __new__() method for the type (as it is for tuples
What is an "internal invariant"? How do I know when the memory allocation
is affected? Does my Test class affect the memory allocation?
> As a sidenote, your class works fine without changing anything when
> pickling/unpickling instead of copying, although pickle calls
> __reduce_ex__ too:
> from pickle import dumps,loads
> t = Test(0, 0)
> assert loads(dumps(t)) == t
> Perhaps someone more knowledgeable can explain the subtle differences
> between pickling and copying here.
I have a situation in the full program where pickling seems to be failing in
the same manner as copy, but I have not been able yet to reproduce the
problem in a simple test program.
Thanks to all for your comments.
More information about the Python-list