使用装饰器和使用静态方法的函数之间的区别



我正在尝试创建一个类,该类被赋予一个函数,然后从该实例运行。但是,当我尝试使用staticmethod时,我发现使用装饰器和仅传递函数staticmethod是有区别的。

class WithDec():
def __init__(self):
pass
@staticmethod
def stat(val):
return val + 1

def OuterStat(val):
return val + 1
class WithoutDec():
def __init__(self, stat):
self.stat = staticmethod(stat)

对于这两个类,将发生以下情况。

>>> WithDec().stat(2)
3
>>> WithoutDec(OuterStat).stat(2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'staticmethod' object is not callable

发生了什么,我能做些什么来阻止它。

静态方法仍然通过描述符协议工作,这意味着当它是属性时,通过实例访问它仍然意味着将调用__get__方法以返回实际调用的对象。那是

WithDec().stat(2)

相当于

w = WithDec()
w.stat(2)

相当于

WithDec.stat.__get__(w, WithDec)(2)

但是,当静态方法是实例属性时,不会调用描述符协议,就像WithoutDec一样。在这种情况下

WithoutDec().stat(2)

尝试调用文字staticmethod实例stat,而不是stat.__get__返回的函数。

你想要的是使用staticmethod来创建类属性,而不是通过装饰器语法:

class WithoutDec():
def stat(val):
return val + 1
stat = staticmethod(stat)

首先将stat绑定到常规函数(在尝试将其用作实例方法之前,它不是真正的实例方法),然后将该函数替换为包装原始函数的staticmethod实例。

问题是您正在尝试在__init__中使用staticmethod(),它用于创建类的实例,而不是直接在类级别定义类、其方法和静态方法。

此代码有效:

def OuterStat(val):
return val + 1
class WithoutDec():
stat = staticmethod(OuterStat)
>>> WithoutDec.stat(2)
3

请注意,尝试使用自己的不同 stat 版本创建WithoutDec实例与静态方法的含义相反。

我在这个线程上找到了一个非常鼓舞人心的解决方案。事实上,你的代码不是很pythonic,并且将静态方法归因于类实例的属性。以下代码有效:

class WithoutDec():

stat = None
@staticmethod
def OuterStat(val):
return val + 1

然后你打电话:

my_without_dec = WithoutDec()
my_without_dec.stat = WithotuDec.OuterStat
my_without_dec.stat(2)

稍后,如果要创建新方法,请调用:

def new_func(val):
return val+1
WithoutDec.newStat = staticmethod(new_func)
my_without_dec.stat = WithoutDec.newStat
my_without_dec.stat(2)

是的- 在这种情况下,您只需将函数添加为实例的属性,它将按预期工作,不需要任何装饰器:

def OuterStat(val):
return val + 1
class WithoutDec():
def __init__(self, stat):
self.stat = stat

问题是:如果函数是的属性还是实例的属性,这是有区别的。当它在带有self.func = X的实例方法中设置时,它成为一个实例属性 - Python 以存储方式检索它,没有修改,它只是对可以调用的原始函数的另一个引用。

相反,当函数存储为属性时,默认行为是将其用作实例方法:从实例检索函数时,Python 会安排内容,以便self作为该函数的第一个参数注入。在这种情况下,装饰器@classmethod存在,@staticmethod修改此行为(对类进行classmethodstaticmethod不进行注入)。

问题是staticmethod不返回函数 - 它返回一个用作类属性的描述符,因此当从类中检索修饰的函数时,它作为一个普通函数工作。

(内部细节:所有 3 种行为:实例方法、类方法和静态方法都是通过在用作类属性的对象上使用适当的__get__方法来实现的)。

注意:有一些关于使"staticmethod"成为"可调用"的讨论,并简单地调用包装的函数 - 我刚刚检查了它是否进入了Pythonn 3.10 beta 1。这意味着您的示例代码将像 Python 3.10 一样工作 - 尽管如此,正如本答案开头所述,那里的 staticmethod 调用是多余的,不应使用。

最新更新