deepcopy chokes with TypeError on dynamically assigned instance method

@t comcast d.t net "kanenas\" at t comcast d.t net
Sat Feb 12 19:12:02 EST 2005


On Fri, 11 Feb 2005 21:22:35 +1000, Nick Coghlan <ncoghlan at iinet.net.au> 
wrote:
 >
 >Interesting. The problem appears to be that bound methods are not 
copyable:
 >
Curiosity got the better of me and I started diggin about in copy.py. 
Turns out that return values of reductors for functions don't include a 
code object or global dict, and reductors for instancemethods don't 
return a function or instance, hence the complaint that instancemethod 
(or function, as below) didn't get enough arguments.

  >>> f = Foo('foo')
  >>> fbrc,fbargs = f.bar.__reduce_ex__(2)[:2]
  >>> fbargs
(<type 'instancemethod'>,)
  >>> fbrc(*fbargs)
  Traceback (most recent call last):
    File "<pyshell#2>", line 1, in -toplevel-
      fbrc(*fbargs)
    File "C:\DEVEL\PYTHON\2.4\lib\copy_reg.py", line 92, in __newobj__
      return cls.__new__(cls, *args)
  TypeError: instancemethod expected at least 2 arguments, got 0
  >>> fbrc(fbargs[0], f.bar, f, type(f))
<bound method Foo.bar of Foo([f,o,o])>
  >>> def bar(a, b):
      return (a,b)
  >>> info = bar.__reduce_ex__(2)
  >>> rc,args = info[:2]
  >>> args
  (<type 'function'>,)
  >>> rc(*args)
  Traceback (most recent call last):
    File "<pyshell#9>", line 1, in -toplevel-
      rc(*args)
    File "C:\DEVEL\PYTHON\2.4\lib\copy_reg.py", line 92, in __newobj__
      return cls.__new__(cls, *args)
  TypeError: function() takes at least 2 arguments (0 given)
  >>> baz=rc(args[0], bar.func_code, bar.func_globals)
  >>> baz(1,2)
  (1,2)

Note 'args' is not something like :
  (<type 'function'>, <function bar at 0xXXXXXXXX>, { ... })

As an aside, what is the tuple returned by a reductor called?  What are 
its components called?

 >Normally, the methods live in the class dictionary, so they don't cause a
 >problem with copying the instance.
 >
But a dynamically assigned instance method lives in the intance 
dictionary, making deepcopy choke when it deepcopies the instance state. 
  That makes sense now.

 >It turns out this exception actually makes sense, since you *don't* 
want to copy
 >these atributes to the new instance. If you actually copied them, 
they'd be
 >bound to the *old* instance, rather than the new one.
 >
True.  It wouldn't cause a problem within my __init__, since the 
attribute is reassigned after the deepcopy, though should anyone else 
deepcopy an instance...  Definitely better that the deepcopy throws the 
TypeError.  But why shouldn't we be able to copy a non-method function?

 >So I expect you'll need to provide a __deepcopy__ in order to 
correctly generate
 >the instancemethods bound to the new instance.
 >
That's what I've decided, too.



More information about the Python-list mailing list