Python 使用decorator和staticmethod函数的区别

Python 使用decorator和staticmethod函数的区别,python,static-methods,Python,Static Methods,我试图创建一个类,该类得到一个函数,然后从该实例运行。然而,当我尝试使用staticmethod时,我发现使用decorator和只传递staticmethod函数之间有区别 class WithDec(): def __init__(self): pass @staticmethod def stat(val): return val + 1 def OuterStat(val): return val + 1 class

我试图创建一个类,该类得到一个函数,然后从该实例运行。然而,当我尝试使用
staticmethod
时,我发现使用decorator和只传递
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
>>with dec().stat(2)
3.
>>>without dec(OuterStat).stat(2)
回溯(最近一次呼叫最后一次):
文件“”,第1行,在
TypeError:“staticmethod”对象不可调用

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

我发现了一个非常鼓舞人心的解决方案。事实上,您的代码不是很pythonic,并且将静态方法属性化为类实例的属性。以下代码起作用:

class without dec():
stat=None
@静力学方法
def OuterStat(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)
稍后,如果要创建新方法,请调用:

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)

问题是您试图在
\uuuu init\uuuu
内部使用
staticmethod()
,它用于创建类的实例,而不是直接在类级别上,定义类、类的方法及其静态方法

此代码适用于:

def OuterStat(val):
返回值+1
不带dec()的类:
stat=staticmethod(OuterStat)
>>无十二月统计(2)
3.
请注意,尝试在没有DEC的情况下创建一个具有自己的、不同的stat版本的
实例与静态方法的含义相反。

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

def OuterStat(val):
返回值+1
不带dec()的类:
定义初始(自我,状态):
self.stat=stat
问题是:函数是类的属性还是实例的属性是有区别的。当它被设置在带有
self.func=X
的实例方法中时,它将成为一个实例属性——Python以存储它的方式检索它,而不做任何修改,它只是对可以调用的原始函数的另一个引用

相反,当函数存储为类attibute时,默认行为是将其用作实例方法:从实例检索函数时,Python会进行安排,以便将
self
作为该函数的第一个参数注入。在这种情况下,修饰符
@classmethod
@staticmethod
存在以修改此行为(为
classmethod
导入类或不对
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)
(内部详细信息:所有3种行为:instance方法、classmethod和staticmethod都是通过在用作类属性的对象上使用适当的
\uuuuuu get\uuuu
方法来实现的)


NB:在将“staticmethod”变为“可调用”的过程中进行了一些讨论,只需调用包装好的函数——我刚刚检查了它,使其成为Python 3.10 beta 1。这意味着您的示例代码将像Python 3.10一样工作-尽管如此,这里的staticmethod调用是多余的,正如本答案的开头部分所述,不应使用它。

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

WithDec().stat(2)
相当于

w = WithDec()
w.stat(2)
WithDec.stat.__get__(w, WithDec)(2)
这相当于

w = WithDec()
w.stat(2)
WithDec.stat.__get__(w, WithDec)(2)
但是,当静态方法是实例属性时,不会调用描述符协议,如
WithoutDec
的情况。那么

WithoutDec().stat(2)
尝试调用literal
staticmethod
实例
stat
,而不是
stat.\uuuu get\uuu
返回的函数

您想要的是使用
staticmethod
创建类属性,而不是通过decorator语法:

class WithoutDec():

    def stat(val):
        return val + 1

    stat = staticmethod(stat)

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

这毫无意义-静态方法怎么可能是实例属性,你为什么要让它成为一个呢?如果希望它属于实例并可从实例调用,只需编写
self.stat=stat
。您在第一个示例中介绍了工作代码。那么,你要什么?
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)