Python:将静态方法分配给类变量会出错 [英] Python: Assigning staticmethod to class variable gives error

查看:22
本文介绍了Python:将静态方法分配给类变量会出错的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 Python 中为类变量分配一个静态方法,下面是我的代码.

I want to assign a static method to a class variable in Python, and below is what my code looks like.

class Klass:
    classVariable = None

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

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

这在最后一行给了我一个错误,TypeError: 未绑定的方法 method() 必须使用 Klass 实例作为第一个参数调用(什么都没有).

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).

但是当我将静态方法更改为类方法时,它可以工作.谁能告诉我为什么会这样?

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)

首先,我们需要了解一些 Python 描述符...

对于这个答案,了解以下内容应该就足够了:

For this answer, it should be enough to know the following:

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

换句话说:

class Foo(object):
    val = some_descriptor

当我这样做时:

result = Foo.val

Python 确实做到了:

Python actually does:

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 的类型来查看:

Now the good stuff.

It looks like (on python2.x), staticmethod is implemented such that it's __get__ method returns a regular function. You can see this by printing the type of Klass.method:

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

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

So what we've learned is that the method returned by Klass.method.__get__ is just a regular function.

当你把那个常规函数放到一个类上时,它的 __get__ 方法返回一个 instancemethod(它需要一个 self 参数).这并不奇怪......我们一直这样做:

When you put that regular function onto a class, it's __get__ method returns an instancemethod (which expects a self argument). This isn't surprising ... We do it all the time:

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

与python没有什么不同:

Is no different to python than:

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. Is there anything we can do about it?

当你把这个方法放到类上时,再次将它指定为一个staticmethod,它就可以运行了.

When you put the method onto the class, designate it as a staticmethod again and it will work out Ok.

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不会遇到这个问题 -- 这里有一个例子:

Appendix -- Re-implementation of @staticmethod

If you're a little but curious how you might implement staticmethod to not have this problem -- Here's an example:

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__ 方法来做正确的事情(传递给委托函数并返回结果).

The trick here is that my __get__ doesn't return a function. It returns itself. When you put it on a different class (or the same class), it's __get__ will still just return itself. 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).

请注意,我不提倡您使用此 StaticMethod 而不是 staticmethod.它会降低效率并且不会那么自省(并且可能会让您的代码阅读器感到困惑).这仅用于教育目的.

这篇关于Python:将静态方法分配给类变量会出错的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆