在类体中调用类staticmethod?

mar*_*eau 134 python static-methods decorator

当我尝试在类的主体内使用静态方法,并使用内置staticmethod函数作为装饰器定义静态方法时,如下所示:

class Klass(object):

    @staticmethod  # use as decorator
    def _stat_func():
        return 42

    _ANS = _stat_func()  # call the staticmethod

    def method(self):
        ret = Klass._stat_func() + Klass._ANS
        return ret
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

Traceback (most recent call last):<br>
  File "call_staticmethod.py", line 1, in <module>
    class Klass(object): 
  File "call_staticmethod.py", line 7, in Klass
    _ANS = _stat_func() 
  TypeError: 'staticmethod' object is not callable
Run Code Online (Sandbox Code Playgroud)

我理解为什么会发生这种情况(描述符绑定),并且可以通过_stat_func()在上次使用后手动转换为static方法来解决它,如下所示:

class Klass(object):

    def _stat_func():
        return 42

    _ANS = _stat_func()  # use the non-staticmethod version

    _stat_func = staticmethod(_stat_func)  # convert function to a static method

    def method(self):
        ret = Klass._stat_func() + Klass._ANS
        return ret
Run Code Online (Sandbox Code Playgroud)

所以我的问题是:

是否更好,如更清洁或更"Pythonic",如何实现这一目标?

Ben*_*Ben 152

staticmethod对象显然有一个__func__存储原始原始函数的属性(他们必须这样做).所以这将有效:

class Klass(object):

    @staticmethod  # use as decorator
    def stat_func():
        return 42

    _ANS = stat_func.__func__()  # call the staticmethod

    def method(self):
        ret = Klass.stat_func()
        return ret
Run Code Online (Sandbox Code Playgroud)

顺便说一句,虽然我怀疑staticmethod对象有某种存储原始函数的属性,但我不知道具体细节.本着教别人钓鱼而不是给他们钓鱼的精神,这就是我所做的调查和发现(我的Python会话中的C&P):

>>> class Foo(object):
...     @staticmethod
...     def foo():
...         return 3
...     global z
...     z = foo

>>> z
<staticmethod object at 0x0000000002E40558>
>>> Foo.foo
<function foo at 0x0000000002E3CBA8>
>>> dir(z)
['__class__', '__delattr__', '__doc__', '__format__', '__func__', '__get__', '__getattribute__', '__hash__', '__init__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
>>> z.__func__
<function foo at 0x0000000002E3CBA8>
Run Code Online (Sandbox Code Playgroud)

在交互式会话中进行类似的挖掘(dir非常有用)通常可以很快解决这些问题.

  • 好的更新......我正想问你是怎么知道的,因为我没有在文档中看到它——这让我对使用它有点紧张,因为它可能是一个“实现细节”。 (2认同)
  • 我明白了,从技术上讲,它在这种背景下没有记载. (2认同)
  • @AkshayHazari 静态方法的 __func__` 属性为您提供对原始函数的引用,就像您从未使用过 'staticmethod' 装饰器一样。因此,如果您的函数需要参数,则必须在调用“__func__”时传递它们。您的错误消息听起来好像您没有给它任何参数。如果本文示例中的 stat_func 有两个参数,您将使用 _ANS = stat_func.__func__(arg1, arg2)` (2认同)

Jan*_*cak 22

这是我喜欢的方式:

class Klass(object):

    @staticmethod
    def stat_func():
        return 42

    _ANS = stat_func.__func__()

    def method(self):
        return self.__class__.stat_func() + self.__class__._ANS
Run Code Online (Sandbox Code Playgroud)

Klass.stat_func由于DRY原则,我更喜欢这种解决方案.让我想起Python 3中有一个新super()原因 :)

但我同意其他人,通常最好的选择是定义模块级功能.

例如使用@staticmethod函数,递归可能看起来不太好(你需要通过调用Klass.stat_func内部来破坏DRY原则Klass.stat_func).那是因为你没有引用self内部静态方法.使用模块级功能,一切看起来都不错.

  • 这本身并不是 DRY 的问题。`self.__class__` 更好,因为如果子类重写 `stat_func`,那么 `Subclass.method` 将调用子类的 `stat_func`。但说实话,在这种情况下,最好使用真正的方法而不是静态方法。 (3认同)

Kei*_*ith 10

这是因为staticmethod是一个描述符,需要一个类级属性fetch来运用描述符协议并获得真正的可调用.

从源代码:

它可以在类(例如C.f())或实例(例如C().f())上调用; 除了类之外,该实例被忽略.

但是在定义时不是直接来自类内部.

但正如一位评论者提到的,这根本不是一个"Pythonic"设计.只需使用模块级功能.

  • 不,我想要做的事实上非常简单 - 它可以分解一些常用代码并重用它,既可以在类创建时创建私有类属性,也可以在以后的一个或多个类方法中创建私有类属性.这个常用代码在课外没有用,所以我自然希望它成为它的一部分.使用元类(或类装饰器)可以工作,但这对于应该很容易做的事情来说似乎有点过头了,恕我直言. (3认同)

Ped*_*ano 9

那么在类定义之后注入class属性呢?

class Klass(object):

    @staticmethod  # use as decorator
    def stat_func():
        return 42

    def method(self):
        ret = Klass.stat_func()
        return ret

Klass._ANS = Klass.stat_func()  # inject the class attribute with static method value
Run Code Online (Sandbox Code Playgroud)

  • 这类似于我第一次尝试解决问题,但我更喜欢类内部的东西......部分原因是所涉及的属性有一个前导下划线并且是私有的。 (2认同)

sch*_*ten 6

这个解决方案怎么样?它不依赖于@staticmethod装饰器实现的知识.内部类StaticMethod作为静态初始化函数的容器.

class Klass(object):

    class StaticMethod:
        @staticmethod  # use as decorator
        def _stat_func():
            return 42

    _ANS = StaticMethod._stat_func()  # call the staticmethod

    def method(self):
        ret = self.StaticMethod._stat_func() + Klass._ANS
        return ret
Run Code Online (Sandbox Code Playgroud)

  • +1创造力,但我不再担心使用`__func__`,因为它现在正式记录(参见[_Other Language Changes_]一节(https://docs.python.org/2/whatsnew/2.7.html# Python 2.7中的新功能及其对[问题5982](http://bugs.python.org/issue5982)的引用的其他语言变化).你的解决方案更具可移植性,因为在2.6之前它可能也适用于Python版本(当`__func__`首次作为`im_func`的同义词引入时). (2认同)