从类外部的现有方法创建 staticmethod?(“unbound method”错误)

Create staticmethod from an existing method outside of the class? ("unbound method" error)

提问人:William Pursell 提问时间:1/19/2012 最后编辑:William Pursell 更新时间:12/3/2016 访问量:226

问:

定义类后,如何使类方法静态?换句话说,为什么第三种情况会失败?

>>> class b:
...  @staticmethod
...  def foo():
...   pass
...
>>> b.foo()
>>> class c:
...  def foo():
...   pass
...  foo = staticmethod( foo )
...
>>> c.foo()
>>> class d:
...  def foo():
...   pass
...
>>> d.foo = staticmethod( d.foo )
>>> d.foo()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method foo() must be called with d instance as first argument (got nothing instead)

评论


答:

5赞 Ignacio Vazquez-Abrams 1/19/2012 #1

必须传递函数,而不是 unbound 方法。

>>> class d:
...   def foo():
...     pass
... 
>>> d.foo = staticmethod(d.foo.im_func)
>>> d.foo()
6赞 kindall 1/19/2012 #2

定义类后,是一个未绑定的方法对象(将函数绑定到类的包装器),而不是函数。包装器将尝试将类 () 的实例作为第一个参数传递给函数,因此它抱怨您没有给它一个。fooself

staticmethod()实际上应该只与函数一起使用。虽然类仍在定义中(如 ur 和 ),但仍然是一个函数,因为当类定义完成时会应用方法包装器。bcfoo()

可以从未绑定的方法对象中提取函数,应用 ,并将其赋回类。staticmethod()

class d(object):
    def foo():
       pass

d.foo = staticmethod(d.foo.im_func)

在 Python 3 中,没有未绑定的实例方法(存储在类上的方法是普通函数)。因此,您的原始代码实际上可以在 Python 3 上运行良好。

一种适用于 Python 2 和 Python 3 的方法是:

d.foo = staticmethod(d.__dict__['foo'])

(它还避免了创建和丢弃方法包装器。

评论

0赞 Óscar López 1/19/2012
那不应该是吗?d.foo.im_func
0赞 ShadowRanger 12/3/2016
好吧,您需要标记它们,以便在实例上等效地调用它们,而不仅仅是类本身。 无论是否使用包装,但会爆炸(它会尝试通过不接受的传递)。当然,没有与实例相关的功能需要在实例上调用它们,但对于 DRY,引用静态方法的方法可以而且应该调用它,而不是显式命名类。staticmethodd.foo()staticmethodd().foo()selffooself.foo()
0赞 ShadowRanger 12/3/2016
此外,using 意味着在 Py2 上创建然后丢弃一个未绑定的方法,如果代码必须在 Py2 和 Py3 上运行(后者甚至在绑定方法上都没有属性,更不用说原始函数了),那么它就不可移植。您可以通过直接访问类字典来完全可移植地绕过未绑定方法的创建,这绕过了描述符协议,使其效率更高,并且完全可移植: .im_funcim_funcd.foo = staticmethod(d.__dict__['foo'])
0赞 kindall 12/3/2016
@ShadowRanger 感谢您的评论。我已经编辑了我的答案。我想我差不多五年前的很多答案也可以得到改进......