关于python:在类体中调用类staticmethod?

Calling class staticmethod within the class body?

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

1
2
3
4
5
6
7
8
9
10
11
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

我得到以下错误:

1
2
3
4
5
6
Traceback (most recent call last):
  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

我理解发生这种情况的原因(描述符绑定),并且可以在上次使用后通过手动将_stat_func()转换为staticmethod来解决这一问题,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
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

所以我的问题是:

有没有更好的方法,如更清洁的或更多的"Python"来实现这一点?


staticmethod对象显然有一个__func__属性来存储原始函数(这是必须的)。所以这是可行的:

1
2
3
4
5
6
7
8
9
10
11
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

顺便提一句,尽管我怀疑StaticMethod对象具有某种存储原始函数的属性,但我不知道具体细节。本着教别人钓鱼而不是给他们一条鱼的精神,我这样做是为了调查并发现这一点(我的Python课程中的C&P):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> 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>

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


我喜欢这样:

1
2
3
4
5
6
7
8
9
10
class Klass(object):

    @staticmethod
    def stat_func():
        return 42

    _ANS = stat_func.__func__()

    def method(self):
        return self.__class__.stat_func() + self.__class__._ANS

由于干燥原理,我更喜欢这个解决方案。提醒我为什么在python 3中有一个新的super():。

但我同意其他人的观点,通常最好的选择是定义一个模块级的函数。

例如,对于@staticmethod函数,递归看起来可能不太好(您需要通过在Klass.stat_func内部调用Klass.stat_func来打破干燥原则)。这是因为在静态方法中没有对self的引用。使用模块级功能,一切都会正常。


这是由于StaticMethod是一个描述符,需要一个类级属性获取来执行描述符协议并获得真正的可调用性。

从源代码:

It can be called either on the class (e.g. C.f()) or on an instance
(e.g. C().f()); the instance is ignored except for its class.

但不能直接从类内部定义。

但正如一位评论家所说,这根本不是一个"Python"设计。只需使用模块级函数。


这个解决方案怎么样?它不依赖于@staticmethoddecorator实现的知识。内部类staticMethod充当静态初始化函数的容器。

1
2
3
4
5
6
7
8
9
10
11
12
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


在类定义之后注入类属性怎么样?

1
2
3
4
5
6
7
8
9
10
11
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


根据下面的博客,在类内调用静态方法时,调用函数必须是类方法,因此在方法定义中添加@classmethod可以解决这个问题。

关于如何在Python中使用静态、类或抽象方法的最终指南