Problem using copy.copy with my own class
George Sakkis
george.sakkis at gmail.com
Thu Apr 24 00:41:18 EDT 2008
On Apr 23, 9:48 pm, Jeffrey Barish <jeff_bar... at earthlink.net> wrote:
>
> Here it is:
>
> import copy
>
> class Test(int):
> def __new__(cls, arg1, arg2):
> return int.__new__(cls, arg1)
>
> def __init__(self, arg1, arg2):
> self.arg2 = arg2
>
> if __name__ == '__main__':
> t = Test(0, 0)
> t_copy = copy.copy(t)
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.
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:
class Test(int):
...
def __copy__(self):
return Test(int(self), self.arg2)
The copy.copy() function looks for this special method and invokes it
if it's defined. 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.
> Traceback (most recent call last):
> File "copytest.py", line 12, in <module>
> t_copy = copy.copy(t)
> File "/usr/lib/python2.5/copy.py", line 95, in copy
> return _reconstruct(x, rv, 0)
> File "/usr/lib/python2.5/copy.py", line 322, in _reconstruct
> y = callable(*args)
> File "/usr/lib/python2.5/copy_reg.py", line 92, in __newobj__
> return cls.__new__(cls, *args)
> TypeError: __new__() takes exactly 3 arguments (2 given)
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:
class Test(int):
def __new__(cls, arg1, arg2=None):
return int.__new__(cls, arg1)
# don't need to define __copy__ now
from copy import copy
t = Test(0, 0)
assert copy(t) == t
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.
George
[1] http://docs.python.org/lib/module-copy.html
[2] http://docs.python.org/lib/node320.html
More information about the Python-list
mailing list