在类体中调用类staticmethod?

时间:2012-10-03 23:12:08

标签: python decorator static-methods

当我尝试在类的主体内使用静态方法,并使用内置的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”的方式来实现这一目标?

7 个答案:

答案 0 :(得分:136)

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

顺便说一句,虽然我怀疑staticmethod对象有某种存储原始函数的属性,但我不知道具体细节。本着教别人钓鱼而不是给他们钓鱼的精神,这就是我所做的调查和发现(我的Python会话中的C&amp; 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非常有帮助)通常可以很快解决这些问题。

答案 1 :(得分:17)

这是我喜欢的方式:

class Klass(object):

    @staticmethod
    def stat_func():
        return 42

    _ANS = stat_func.__func__()

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

由于DRY principle,我更喜欢这个解决方案Klass.stat_func。 让我想起Python 3中的reason why there is a new super():)

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

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

答案 2 :(得分:9)

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

来自源代码:

  

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

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

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

答案 3 :(得分:8)

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

答案 4 :(得分: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

答案 5 :(得分:1)

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

The definitive guide on how to use static, class or abstract methods in Python

答案 6 :(得分:0)

如果“核心问题”是使用函数分配类变量,则另一种方法是使用元类(它是“烦人的”和“魔术的”,我同意应该在类内部调用静态方法,但不幸的是,不是)。这样,我们可以将行为重构为一个独立的函数,而不会使类混乱。

export class AppComponent implements OnInit {
  constructor(private invokeService: CallWSService) { }
  resultService: object;

  ngOnInit(): void {
    this.invokeService.callGetMethodExample("http://dummy.restapiexample.com/api/v1/employees",this.responseFunc);
  }

  responseFunc (result: any) : any {
    console.log(result);
    this.resultService = result;
    console.log(this.resultService);
  }

  doInError (result: any) : any {
    console.log(result);
  }
}

在“现实世界”中使用此替代方法可能会出现问题。我不得不使用它来覆盖Django类中的类变量,但是在其他情况下,最好使用其他答案中的一种。