Python:将staticmethod分配给类变量会产生错误

时间:2016-05-13 01:23:16

标签: python static-methods class-method

我想在Python中为类变量分配静态方法,下面是我的代码的样子。

class Klass:
    classVariable = None

    @staticmethod
    def method():
        print "method called"        

Klass.classVariable = Klass.method        
Klass.method()
Klass.classVariable()

这在最后一行给了我一个错误, TypeError: unbound method method() must be called with Klass instance as first argument (got nothing instead).

但是当我将静态方法更改为类方法时,它可以工作。任何人都可以告诉我为什么会这样吗?

1 个答案:

答案 0 :(得分:6)

Backstory(描述符协议)

首先,我们需要了解一下python descriptors ...

对于这个答案,应该知道以下内容:

  1. 功能是描述符。
  2. 通过函数的self方法和内置描述符协议实现方法的绑定行为(即方法如何知道要传递的__get__)。
  3. 当您在类上放置描述符foo时,访问该描述符实际上会调用.__get__方法。(这实际上只是语句2的概括)
  4. 换句话说:

    class Foo(object):
        val = some_descriptor
    

    当我这样做时:

    result = Foo.val
    

    Python实际上是这样做的:

    Foo.val.__get__(None, Foo)
    

    当我这样做时:

    f = Foo()
    f.val
    

    python确实:

    f = Foo()
    type(f).val.__get__(f, type(f))
    

    现在好东西。

    看起来(在python2.x上),staticmethod被实现,使得__get__方法返回常规函数。您可以通过打印Klass.method

    的类型来查看此内容
    print type(Klass.method)  # <type 'function'>
    

    所以我们学到的是Klass.method.__get__返回的方法只是一个常规函数。

    当您将该常规函数放到类上时,它的__get__方法返回instancemethod(期望self参数)。这并不奇怪......我们一直这样做:

    class Foo(object):
        def bar(self):
            print self
    

    与python没有什么不同:

    def bar(self):
        print self
    
    class Foo(object):
        pass
    
    Foo.bar = bar
    

    除了第一个版本更易于阅读并且不会使您的模块名称空间混乱。

    所以现在我们已经解释了你的staticmethod如何变成一个实例方法。我们能做些什么吗?

    解决方案

    当你把这个方法放到课堂上时,再次将它指定为staticmethod,这样就可以了。

    class Klass(object):  # inheriting from object is a good idea.
        classVariable = None
    
        @staticmethod
        def method():
            print("method called")
    
    Klass.classVariable = staticmethod(Klass.method)  # Note extra staticmethod
    Klass.method()
    Klass.classVariable()
    

    附录 - 重新实施@staticmethod

    如果你有点但很好奇如何实现staticmethod 而不是有这个问题 - 以下是一个例子:

    class StaticMethod(object):
        def __init__(self, fn):
            self.fn = fn
    
        def __get__(self, inst, cls):
            return self
    
        def __call__(self, *args, **kwargs):
            return self.fn(*args, **kwargs)
    
    
    class Klass(object):
        classVariable = None
    
        @StaticMethod
        def method():
            print("method called")
    
    Klass.classVariable = Klass.method
    Klass.method()
    Klass.classVariable()
    Klass().method()
    Klass().classVariable()
    

    这里的诀窍是我的__get__没有返回一个函数。它返回本身。当你把它放在一个不同的类(或同一个类)上时,它的__get__仍会自行返回。由于它从__get__返回,它需要伪装成一个函数(所以它可以在“__gotten__”之后调用)所以我实现了一个自定义的__call__方法来做正确的事情(通过到委托函数并返回结果)。

    请注意,我并不主张您使用此StaticMethod代替staticmethod 。它的效率会降低,而且不会过于苛刻(并且可能会让您的代码读者感到困惑)。这仅用于教育目的。