当我尝试在类主体中使用静态方法,并使用内置
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()
转换为静态方法来解决它,如下所示:
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”的方法来完成此任务?
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 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_func
内部调用 Klass.stat_func
来打破 DRY 原则)。那是因为您在静态方法中没有引用 self
。
有了模块级功能,一切看起来都OK了。
这是因为 staticmethod 是一个描述符,需要类级别的属性获取来执行描述符协议并获得真正的可调用。
来自源代码:
它可以在类(例如
)或实例上调用 (例如C.f()
);除了其类之外,该实例将被忽略。C().f()
但在定义类时不能直接从类内部进行。
但正如一位评论者提到的,这根本不是真正的“Pythonic”设计。只需使用模块级函数即可。
在类定义之后注入类属性怎么样?
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
这个解决方案怎么样?它不依赖于
@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
如果“核心问题”是使用函数分配类变量,另一种方法是使用元类(这有点“烦人”和“神奇”,我同意静态方法应该在类内部可调用,但不幸的是它不是't)。这样,我们可以将行为重构为独立的函数,并且不会使类变得混乱。
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 类中的类变量,但在其他情况下,也许最好使用其他答案中的替代方案之一。
class Tmp:
@staticmethod
def func1():
return 1234
X = func1()
print(Tmp.X)
Python 3.12.2 不太好
class Tmp:
@staticmethod
def func1():
return 1234
@staticmethod
def func2():
# Tmp.X2=Tmp.func1()+4444 # AttributeError: type object 'Tmp' has no attribute 'X2'
return Tmp.func1()+4444
# X2 = func2() # NameError: name 'Tmp' is not defined
Tmp.X2 = Tmp.func2() #print 5678
print(Tmp.X2)