Python 装饰器作为静态方法
Posted
技术标签:
【中文标题】Python 装饰器作为静态方法【英文标题】:Python decorator as a staticmethod 【发布时间】:2011-09-18 17:19:12 【问题描述】:我正在尝试编写一个 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
作为第一个参数吗?
【问题讨论】:
为什么你认为你需要@staticmethod
?很明显,你不明白这意味着什么。静态方法未绑定到对象的实例,因此它们没有 self
参数(并且无法访问实例变量)。
@Nick:装饰器ensure_black
不需要访问self
。它只需要访问func
。
@Sven: 好点子 - 我被def get()
也没有使用self
的脸吓到了,所以对_aux
怎么可能感到困惑。
你能看看***.com/questions/51260036/… 并发布你是如何在没有构造函数的情况下使它工作的吗?
【参考方案1】:
这不是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
放在课堂主体的末尾——在这种情况下,它不打算在课堂外使用。
【讨论】:
好的,只是 ensure_black 在这个类之外有零使用。我很高兴删除@staticmethod
,我只是想知道它为什么有效。如果没有 staticmethod 装饰器,那么 ensure_black 仍然是静态方法吗?
@wkz:不,它只是一个在TFord
类范围内定义的普通 Python 函数。无论如何,类范围内的函数并不特殊。只有通过ford_black.get
中的实例访问它们时,才会对它们进行特殊处理。这个表达式不会简单地返回类范围内定义的函数get()
,而是一个“绑定方法包装器”,确保在调用函数时正确的实例作为self
参数传递——见上面的链接和例子。该示例通过使用A.__dict__["f"]
来规避特殊处理。
marnach:谢谢!这就是我一直在寻找的答案!
@SvenMarnach 我正在做 OP 正在做的事情,所以你的回答很有帮助,谢谢。
从技术上讲,静态方法对象不使用描述符,它们是描述符,假设定义为“如果__get__
, ...已实施”的立场。【参考方案2】:
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'
【讨论】:
【参考方案3】:解决方案确实存在!
问题是试图用作装饰器的静态方法实际上是静态方法对象并且不可调用。
解决方案: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 _returnBaseAndResult(func):
from functools import wraps
@wraps(func)
def wrapper(*args, **kwargs):
self = args[0]
response = func(*args, **kwargs)
return self.n, response
return wrapper
@_returnBaseAndResult.__get__('this can be anything')
def square(self):
return self.n**2
if __name__ == '__main__':
a = A()
print(a.square())
将打印 (2, 4)
【讨论】:
【参考方案4】:ensure_black
正在返回一个未被@staticmethod
修饰的_aux
方法
您可以将非静态方法返回给 static_method
http://docs.python.org/library/functions.html#staticmethod
【讨论】:
不,这与它无关。 当然有..如果他使用@staticmethod self 在他的方法上下文中不可用..(尽管他的代码因此不起作用)以上是关于Python 装饰器作为静态方法的主要内容,如果未能解决你的问题,请参考以下文章
面向对象编程思想 以及 封装,继承,多态 和 python中实例方法,类方法,静态方法 以及 装饰器