Problem using copy.copy with my own class
Jeffrey Barish
jeff_barish at earthlink.net
Thu Apr 24 12:17:35 EDT 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
warning?
> One way to make this work is to define the special __copy__ method
> [1], 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
> copyable.
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 [2] 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
> first:
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
and strings).
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.
--
Jeffrey Barish
More information about the Python-list
mailing list