简体   繁体   English

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

[英]Python: Assigning staticmethod to class variable gives error

I want to assign a static method to a class variable in Python, and below is what my code looks like. 我想在Python中为一个类变量赋一个静态方法,下面就是我的代码。

class Klass:
    classVariable = None

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

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

This gave me an error at the last line, TypeError: unbound method method() must be called with Klass instance as first argument (got nothing instead). 这在最后一行给出了一个错误, TypeError: unbound method method() must be called with Klass instance as first argument (got nothing instead).

But when I change the static method to class method it works. 但是当我将静态方法更改为类方法时,它可以工作。 Can anyone give me any idea of why this is the case? 任何人都可以告诉我为什么会这样吗?

Backstory (descriptor protocol) 背景故事(描述符协议)

First, we need to know a little about python descriptors ... 首先,我们需要了解一些关于python 描述符 ...

For this answer, it should be enough to know the following: 对于这个答案,应该知道以下内容:

  1. Functions are descriptors. 函数是描述符。
  2. Binding behavior of methods (ie how a method knows what self to pass) is implemented via the function's __get__ method and the built-in descriptor protocol. 方法的绑定行为(即方法如何知道self要通过什么)是通过函数的__get__方法和内置描述符协议实现的。
  3. When you put a descriptor foo on a class, accessing the descriptor actually calls the .__get__ method. 当你在类上放置描述符foo时,访问描述符实际上会调用.__get__方法。 (This is really just a generalization of statement 2) (这实际上只是声明2的概括)

In other words: 换一种说法:

class Foo(object):
    val = some_descriptor

When I do: 当我做:

result = Foo.val

Python actually does: Python实际上做了:

Foo.val.__get__(None, Foo)

When I do: 当我做:

f = Foo()
f.val

python does: python做:

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

Now the good stuff. 现在好东西。

It looks like (on python2.x), staticmethod is implemented such that it's __get__ method returns a regular function. 它看起来像(在python2.x上),实现了staticmethod ,使得它的__get__方法返回一个常规函数。 You can see this by printing the type of Klass.method : 你可以通过打印Klass.method的类型来看到这个:

print type(Klass.method)  # <type 'function'>

So what we've learned is that the method returned by Klass.method.__get__ is just a regular function. 所以我们学到的是Klass.method.__get__返回的方法只是一个常规函数。

When you put that regular function onto a class, it's __get__ method returns an instancemethod (which expects a self argument). 当你将那个常规函数放到一个类上时,它的__get__方法返回一个instancemethod (它需要一个self参数)。 This isn't surprising ... We do it all the time: 这并不奇怪...我们一直这样做:

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

Is no different to python than: 与python没有什么不同:

def bar(self):
    print self

class Foo(object):
    pass

Foo.bar = bar

except that the first version is a lot easier to read and doesn't clutter your module namespace. 除了第一个版本更容易阅读并且不会使您的模块命名空间混乱。

So now we've explained how your staticmethod turned into an instance method. 所以现在我们已经解释了你的staticmethod如何变成一个实例方法。 Is there anything we can do about it? 我们能做些什么吗?

Solution

When you put the method onto the class, designate it as a staticmethod again and it will work out Ok. 当你把方法放到类上时,再次将它指定为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()

Appendix -- Re-implementation of @staticmethod 附录 - 重新实现@staticmethod

If you're a little but curious how you might implement staticmethod to not have this problem -- Here's an example: 如果你是一个有点好奇,但你可能会如何实现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()

The trick here is that my __get__ doesn't return a function. 这里的诀窍是我的__get__没有返回一个函数。 It returns itself . 它返回自己 When you put it on a different class (or the same class), it's __get__ will still just return itself. 当你把它放在一个不同的类(或同一个类)时,它的__get__仍将只返回自己。 Since it is returning itself from __get__ , it needs to pretend to be a function (so it can be called after "__gotten__") so I implement a custom __call__ method to do the right thing (pass through to the delegate function and return the result). 因为它从__get__返回,所以它需要假装是一个函数(所以它可以在“__gotten__”之后调用)所以我实现了一个自定义的__call__方法来做正确的事情(传递给委托函数并返回结果) )。

Please note, I'm not advocating that you use this StaticMethod instead of staticmethod . 请注意, 我并不是说你使用这个StaticMethod而不是staticmethod It'll be less efficient and not as introspectible (and probably confusing for your code readers). 它的效率会降低,而且不会过于苛刻(并且可能会让您的代码读者感到困惑)。 This is only for educational purposes. 这仅用于教育目的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM