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 -- http://mail.python.org/mailman/listinfo/python-list