在类主体中调用类staticmethod?


159

当我尝试从类的主体中使用静态方法,并使用内置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

我收到以下错误:

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

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

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

所以我的问题是:

是否有更好的方法(如更清洁或更“ Pythonic”的)来完成此任务?


4
如果您询问Pythonicity,那么根本不建议使用标准建议staticmethod。它们通常作为模块级功能更有用,在这种情况下,您的问题就不成问题了。classmethod
本杰明·霍奇森

1
@poorsod:是的,我知道这种选择。但是,在我遇到此问题的实际代码中,使函数成为静态方法而不是将其置于模块级别比在我的问题中使用的简单示例更有意义。
martineau 2012年

Answers:


179

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

顺便说一句,尽管我怀疑静态方法对象具有某种存储原始功能的属性,但我对具体细节一无所知。本着教别人钓鱼而不是给他们钓鱼的精神,这就是我所做的调查,并发现了这一点(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>

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


良好的更新...我只是想问问您如何知道这一点,因为我在文档中没有看到它-这使我对使用它有点紧张,因为它可能是“实现细节”。
martineau,2012年

进一步阅读后,我发现这__func__只是im_funcPy 2.6的另一个名称,并且已添加到Py 2.6中以实现Python 3的向前兼容性。
martineau 2012年

2
我看到了,因此从技术上讲,在这种情况下没有记录。
martineau 2012年

1
@AkshayHazari __func__静态方法的属性为您提供了对原始函数的引用,就像您从未使用过staticmethod装饰器一样。因此,如果您的函数需要参数,则在调用时必须传递参数__func__。错误消息听起来很像您没有给它任何参数。如果stat_func在这个岗位的例子了两个参数,你可以使用_ANS = stat_func.__func__(arg1, arg2)

1
@AkshayHazari我不确定我是否理解。它们可以是变量,它们只必须是范围内变量:可以在定义类的相同范围内更早定义(通常是全局),也可以在类范围内更早定义(stat_func本身就是这样的变量)。您是说不能使用所定义类的实例属性吗?没错,但这并不奇怪。我们不具有的类的实例,因为我们仍然定义的类!但是无论如何,我只是将它们作为您想要传递的任何参数的替代品。您可以在那里使用文字。

24

这是我更喜欢的方式:

class Klass(object):

    @staticmethod
    def stat_func():
        return 42

    _ANS = stat_func.__func__()

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

Klass.stat_func由于DRY原理,我更喜欢这种解决方案。让我想起了Python 3中有新功能super()原因 :)

但是我与其他人一样,通常最好的选择是定义一个模块级别的功能。

例如带@staticmethod功能的递归可能看起来不太好(您需要通过调用Klass.stat_funcinside 来打破DRY原理Klass.stat_func)。那是因为您没有引用self内部静态方法。有了模块级功能,一切都会看起来不错。


尽管我同意使用self.__class__.stat_func()常规方法相对于使用具有优势(DRY和所有优点)Klass.stat_func(),但这并不是我所要讨论的话题-实际上,我避免使用前者以免混淆不一致的问题。
martineau 2014年

1
本身并不是真正的DRY问题。self.__class__更好,因为如果子类覆盖stat_funcSubclass.method则将调用子类的stat_func。但老实说,在那种情况下,最好只使用一种真实的方法,而不要使用静态的方法。
asmeurer

@asmeurer:我不能使用一个真正的方法,因为还没有创建该类的实例–因为该类本身甚至还没有完全定义,所以不能。
martineau

我想要一种方法来为我的类调用静态方法(该方法是继承的;因此父级实际上具有该函数),到目前为止,这似乎是最好的(如果以后再覆盖它,它将仍然有效)。
whitey04

11

在类定义之后注入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

1
这类似于我的第一次尝试,但是我更喜欢类内部的内容……部分原因是所涉及的属性带有下划线并且是私有的。
martineau,2012年

11

这是由于staticmethod是描述符,并且需要获取类级别的属性才能执行描述符协议并获得真正的可调用对象。

从源代码:

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

但是在定义类时,不能直接从类内部进行。

但是正如一位评论者所提到的,这根本不是一个真正的“ Pythonic”设计。只需使用模块级功能即可。


正如我在问题中所说的那样,我理解为什么原始代码不起作用。您能解释一下(或提供指向该功能的链接)为什么被认为是非Python的吗?
martineau 2012年

静态方法要求类对象本身才能正常工作。但是,如果您在类级别的类中从类中调用它,那么该类实际上还没有完全定义。所以你还不能引用它。定义之后,可以从类外部调用staticmethod。但是“ Pythonic ”实际上没有很好的定义,很像美学。我可以告诉您,我对该代码的第一印象并不好。
Keith

印象哪个版本(或两者兼有)?又为什么呢?
martineau 2012年

我只能猜测您的最终目标是什么,但是在我看来,您想要一个动态的类级属性。这看起来像是元类的工作。但是,还是有一种更简单的方法,或者是另一种查看设计的方法,它可以在不牺牲功能的情况下消除和简化。
基思2012年

3
不,我要做的实际上很简单-分解出一些通用代码并重用它们,既可以在创建类时创建一个私有类属性,又可以在一个或多个类方法中使用。该通用代码在类外没有用,所以我自然希望它成为其中的一部分。使用元类(或类装饰器)可以工作,但是恕我直言,这对于应该很容易做到的事情来说似乎已经过头了。
martineau 2012年

8

那这个解决方案呢?它不依赖@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

2
+1可以提高创造力,但是我不再担心使用__func__它,因为它已经正式记录在案(请参阅Python 2.7新增功能的其他语言更改及其对Issue 5982的引用)。您的解决方案甚至更具可移植性,因为它可能也可以在2.6之前的Python版本(__func__最初作为的同义词引入im_func)中工作。
martineau 2014年

这是适用于Python 2.6的唯一解决方案。
benselme

@benselme:我无法验证您的主张,因为我没有安装Python 2.6,但严重怀疑它是唯一的一个……
martineau
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.