我正在尝试编写一个python类,它使用需要实例状态信息的装饰器函数。这是按预期工作,但如果我明确地使装饰器成为静态调试,我会收到以下错误:
Traceback (most recent call last):
File "tford.py", line 1, in <module>
class TFord(object):
File "tford.py", line 14, in TFord
@ensure_black
TypeError: 'staticmethod' object is not callable
为什么?
以下是代码:
class TFord(object):
def __init__(self, color):
self.color = color
@staticmethod
def ensure_black(func):
def _aux(self, *args, **kwargs):
if self.color == 'black':
return func(*args, **kwargs)
else:
return None
return _aux
@ensure_black
def get():
return 'Here is your shiny new T-Ford'
if __name__ == '__main__':
ford_red = TFord('red')
ford_black = TFord('black')
print ford_red.get()
print ford_black.get()
如果我只删除行@staticmethod
,一切正常,但我不明白为什么。它不应该self
作为第一个参数吗?
答案 0 :(得分:41)
这不是staticmethod
的使用方式。 staticmethod
个descriptors对象返回包装对象,因此只有在classname.staticmethodname
访问时才能使用它们。实施例
class A(object):
@staticmethod
def f():
pass
print A.f
print A.__dict__["f"]
打印
<function f at 0x8af45dc>
<staticmethod object at 0x8aa6a94>
在A
的范围内,你总是得到后一个不可调用的对象。
我强烈建议将装饰器移动到模块范围 - 它似乎不属于类。如果你想把它放在课堂里,不要把它变成staticmethod
,而只是简单地del
在课堂体的末尾 - 它不是要在课外使用在这种情况下。
答案 1 :(得分:7)
在评估类声明的内容之后,在运行时创建Python类。通过将所有声明的变量和函数分配给特殊字典并使用该字典调用type.__new__
来评估该类(请参阅customizing class creation)。
所以,
class A(B):
c = 1
相当于:
A = type.__new__("A", (B,), {"c": 1})
当您使用@staticmethod注释方法时,在使用type.__new__
创建类之后会发生一些特殊的魔法。在类声明范围内,@ staticmethod函数只是staticmethod对象的一个实例,您无法调用它。装饰器可能只应在同一模块中的类定义之上或单独的“装饰”模块中声明(取决于你有多少个装饰器)。通常,装饰器应该在类之外声明。一个值得注意的例外是属性类(请参阅properties)。在你的情况下,如果你有类似颜色类的东西,那么在类声明中有装饰器可能是有意义的:
class Color(object):
def ___init__(self, color):
self.color = color
def ensure_same_color(f):
...
black = Color("black")
class TFord(object):
def __init__(self, color):
self.color = color
@black.ensure_same_color
def get():
return 'Here is your shiny new T-Ford'
答案 2 :(得分:0)
今天,我希望仅在类内部使用静态方法装饰器。
问题是试图用作装饰器的静态方法实际上是staticmethod对象,并且不可调用。
解决方案:静态方法对象具有方法__get__
,该方法接受任何参数并返回实际方法:python documentation Python 3.5及更高版本:
class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
return self.f
我附带的最小解决方案是:
class A():
def __init__(self):
self.n = 2
@staticmethod
def _addtoglobaltime(func):
from functools import wraps
@wraps(func)
def wrapper(*args, **kwargs):
self = args[0]
response = func(*args, **kwargs)
return self.n, response
return wrapper
@_addtoglobaltime.__get__('this can be anything')
def get(self):
return self.n**2
if __name__ == '__main__':
a = A()
print(a.get())
将打印(2,4)
答案 3 :(得分:-1)
ensure_black
返回的_aux
方法未被@staticmethod
您可以将非静态方法返回到static_method