当我试图从类的主体中使用静态方法,并使用内置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):
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来解决它,就像这样:
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__属性来存储原始的原始函数(必须这样做是有意义的)。这是可行的:
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):
>>> 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非常有用)通常可以很快地解决这类问题。
如果“核心问题”是使用函数分配类变量,另一种选择是使用元类(这有点“烦人”和“神奇”,我同意静态方法应该在类中调用,但不幸的是它不是)。通过这种方式,我们可以将行为重构为一个独立的函数,而不会使类变得混乱。
class KlassMetaClass(type(object)):
@staticmethod
def _stat_func():
return 42
def __new__(cls, clsname, bases, attrs):
# Call the __new__ method from the Object metaclass
super_new = super().__new__(cls, clsname, bases, attrs)
# Modify class variable "_ANS"
super_new._ANS = cls._stat_func()
return super_new
class Klass(object, metaclass=KlassMetaClass):
"""
Class that will have class variables set pseudo-dynamically by the metaclass
"""
pass
print(Klass._ANS) # prints 42
在“现实世界”中使用这种替代方法可能会有问题。我不得不使用它来覆盖Django类中的类变量,但在其他情况下,也许使用其他答案中的一个替代品会更好。