Python - 调用覆盖方法时发生TypeError

时间:2011-08-26 04:38:39

标签: python

我遇到了方法覆盖的问题。

查看下面的src代码,

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__', 
'__str__', '__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)

有趣的是,Foo.bar.im_func属性实际上是方法,并且在调用时工作得很好。我想知道是否有办法用im_func属性恢复Foo.bar方法?请指教〜

谢谢!

2 个答案:

答案 0 :(得分:6)

如果您希望它继续是静态方法,那么在分配时必须告诉Python。否则它将成为一种正常的方法。

Foo.bar = staticmethod(backup)

答案 1 :(得分:3)

如果将一个函数作为一个属性放到一个对象上,事情会变得混乱:由于它们的__get__()方法,它们的行为与它们出现的不同。为了正确理解,您应该阅读Python文档中的the section about data descriptors

一个函数是一个__get__()方法,它将使该函数显示为方法。

假设你有

def f(a): pass
class C(object):
    def m(a): pass # method
    sm = staticmethod(m)
    cm = classmethod(m)
o = C()
  • f然后是<function f at 0xb742c87c>C.__dict__['m']也一样。
  • f.__get__(None, object)返回方法对象<unbound method object.f>。如果被调用,它会检查是否使用给定类的实例调用它,在本例中为object。如果属性访问包含函数作为方法的类,或使用C.m,则相当于C.__dict__['m'].__get__(None, C)并生成<unbound method C.m>
  • f.__get__(4, object)生成<bound method object.f of 4>。它将调用绑定到对象4的原始函数,并在每次调用时将其作为第一个参数传递。这种情况发生在对类实例的属性访问中。因此,o.m会调用C.__dict__['m'].__get__(o, C)并产生<bound method C.m of <__main__.C object at 0x...>>

简而言之:

>>> 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>>

如果将staticmethod()应用于某个函数,然后将其分配给该类中的属性,则会得到类似的行为:staticmethod对象是__get__()不生成的对象方法对象但原始函数本身。参见

>>> 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>

要完成,classmethod()的工作原理如下:

>>> 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'>>

那就是说,你可以看到你的情况会发生什么:首先,你有一个静态的方法。但是

backup = Foo.bar

您获得原始功能,但未应用staticmethod。在回来的路上,

Foo.bar = backup

,将函数赋值给属性,使其成为具有上述含义的类的普通方法,需要使用Foo实例调用(即使函数本身不接受任何参数,这不是方法对象的业务)并自动调用它,其他参数前置于其他参数。