Python - A TypeError occurs calling a overwritten method
I'm running into a problem with method overwriting.
Look at the src code below,
class Foo(object):
@staticmethod
def bar():
pass
Foo.bar() # works fine
print Foo.bar # <function bar at 0x028A5B30>
print dir(Foo.bar)
"""
['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__',
'__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__',
'__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__',
'__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict',
'func_doc', 'func_globals', 'func_name']
"""
backup = Foo.bar # keep the instance of the method object
Foo.bar = backup # overwrite with the same method object
print Foo.bar # <unbound method Foo.bar>
print dir(Foo.bar)
"""
['__call__', '__class__', '__cmp__', '__delattr__', '__doc__', '__format__',
'__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__',
'__reduce__', '__reduce_ex__', '__repr__', '__self__', '__setattr__', '__sizeof__',
'__st开发者_开发知识库r__', '__subclasshook__', 'im_class', 'im_func', 'im_self']
"""
Foo.bar() # TypeError: unbound method foo() must be called with Test instance as first argument (got nothing instead)
It is interesting that the Foo.bar.im_func attribute is actually the method and works just fine when invoked. I'm wondering if there's a way to recover the Foo.bar method with its im_func attribute? Please advice~
Thanks!
If you want it to continue to be a static method then you must tell Python so when assigning. Otherwise it will become a normal method.
Foo.bar = staticmethod(backup)
If you put a function as an attribute to an object, things get confusing: due to their __get__()
methods, they behave differently than they appear. In order to understand that correctly, you should read the section about data descriptors in the Python docs.
A function as is has a __get__()
method which will make the function appear as a method.
Suppose you have
def f(a): pass
class C(object):
def m(a): pass # method
sm = staticmethod(m)
cm = classmethod(m)
o = C()
f
is then<function f at 0xb742c87c>
, as isC.__dict__['m']
.f.__get__(None, object)
returns a method object<unbound method object.f>
. If called, it checks if it is called with an instance of the given class, in this caseobject
. This happens with an attribute access a class containing a function as a method, or withC.m
, which is equivalent toC.__dict__['m'].__get__(None, C)
and yields<unbound method C.m>
.f.__get__(4, object)
produces a<bound method object.f of 4>
. which will call the original function bound to the object4
, passing it as first parameter upon every call. This happens with an attribute access to an instance of the class.o.m
, consequently, callsC.__dict__['m'].__get__(o, C)
and yields<bound method C.m of <__main__.C object at 0x...>>
.
In short:
>>> C.__dict__['m']
<function m at 0xb73bcd84>
>>> C.m
<unbound method C.m>
>>> C.m.im_class, C.m.im_self
(<class '__main__.C'>, None)
>>> C.__dict__['m'].__get__(None, C)
<unbound method C.m>
>>> o.m
<bound method C.m of <__main__.C object at 0xb73c64ac>>
>>> o.m.im_class, o.m.im_self
(<class '__main__.C'>, <__main__.C object at 0xb73c64ac>)
>>> C.__dict__['m'].__get__(o, C)
<bound method C.m of <__main__.C object at 0xb73c64ac>>
If you apply staticmethod()
to a function and then assign it to an attribute in the class, you get similiar behaviour: a staticmethod
object is an object whose __get__()
does not produce a method object but the original function itself. See
>>> C.__dict__['sm']
<staticmethod object at 0xb73cd62c>
>>> C.sm
<function m at 0xb73bcd84>
>>> C.__dict__['sm'].__get__(None, C)
<function m at 0xb73bcd84>
>>> o.sm
<function m at 0xb73bcd84>
>>> C.__dict__['sm'].__get__(o, C)
<function m at 0xb73bcd84>
To be complete, here's how classmethod()
works:
>>> C.__dict__['cm']
<classmethod object at 0xb73cd794>
>>> C.cm
<bound method type.m of <class '__main__.C'>>
>>> C.cm.im_class, C.cm.im_self
(<type 'type'>, <class '__main__.C'>)
>>> o.cm
<bound method type.m of <class '__main__.C'>>
>>> o.cm.im_class, o.cm.im_self
(<type 'type'>, <class '__main__.C'>)
>>> C.__dict__['cm'].__get__(type(C), C)
<bound method type.m of <class '__main__.C'>>
That said, you can see what happens in your case: first, you have a static method. But with
backup = Foo.bar
you get the original function, without staticmethod
applied. On the way back,
Foo.bar = backup
, you assign the function to the attribute, making it an ordinary method of the class with the implications shown above, requiring to be called with a Foo
instance (even if the function itself does not take any parameters, that is not the method object's business) and calling it automatically with another argument prepended to the others.
精彩评论