一尘不染

指向Python中静态方法的指针

python

为什么在下面的代码中,使用类变量作为方法指针会导致未绑定的方法错误,而使用普通变量则可以正常工作:

class Cmd: 
    cmd = None

    @staticmethod   
    def cmdOne():
        print 'cmd one'

    @staticmethod   
    def cmdTwo():
        print 'cmd two'

def main():
    cmd = Cmd.cmdOne
    cmd() # works fine

    Cmd.cmd = Cmd.cmdOne        
    Cmd.cmd() # unbound error !!

if __name__=="__main__":
    main()

完整错误:

TypeError: unbound method cmdOne() must be called with Cmd instance as 
           first argument (got nothing instead)

阅读 340

收藏
2021-01-20

共1个答案

一尘不染

我喜欢从下至上查看这种行为。

Python中的函数充当“描述符对象”。因此,它具有一种__get__()方法。

对具有此类__get__()方法的类属性的读取访问将“重定向”到该方法。对类的属性访问以方式执行attribute.__get__(None, containing_class),而对实例的属性访问则映射到attribute.__get__(instance, containing_class)

函数的__get__()方法的任务是将函数包装在一个方法对象中,该对象将包装self参数-对于对实例的属性访问而言。这称为绑定方法。

在2.x上进行类属性访问时,函数__get__()将返回一个未绑定的方法包装器,而正如我今天了解的那样,在3.x上它将返回自身。(请注意,该__get__()机制在3.x中仍然存在,但是函数只是返回自身。)如果您看一下如何调用它,则几乎是相同的,但是未绑定的方法包装器还会检查self参数的正确类型。

一个staticmethod()呼叫只是创建一个对象,它__get__()调用用于返回最初给出的对象,使其撤销所描述的行为。这就是HYRY的诀窍的工作方式:属性acces取消staticmethod()包装,调用再次进行该操作,以便“新”属性与旧属性具有相同的状态,尽管在这种情况下,staticmethod()似乎应用了两次(但实际上不是)
)。

(顺便说一句:它甚至可以在这种怪异的情况下工作:

s = staticmethod(8)
t = s.__get__(None, 2) # gives 8

虽然8不是函数,2也不是类。)

在您的问题中,您有两种情况:

cmd = Cmd.cmdOne
cmd() # works fine

访问该类并询问其cmdOne属性,即staticmethod()对象。通过其查询__get__()并返回原始函数,然后调用原始函数。这就是为什么它可以正常工作的原因。

Cmd.cmd = Cmd.cmdOne
Cmd.cmd() # unbound error

执行相同的操作,但是将此功能分配给Cmd.cmd。下一行是属性访问-
再次__get__()执行对函数本身的调用,因此返回未绑定的方法,必须以正确的self对象作为第一个参数来调用该方法。

2021-01-20