【问题标题】:Python: Assigning staticmethod to class variable gives errorPython:将静态方法分配给类变量会出错
【发布时间】:2016-05-13 01:23:15
【问题描述】:

我想在 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).

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

【问题讨论】:

  • 有趣的问题。毫不奇怪,它在 python3.x 中工作(如果你修复了 print 语句)......
  • 可能是python2中装饰器的错误
  • @Natecat -- 我不认为这是一个错误,也绝对不是 decorators 的错误。我想有人可能会说这是staticmethod 的一个错误...

标签: python static-methods class-method


【解决方案1】:

背景故事(描述符协议)

首先,我们需要了解一点pythondescriptors...

对于这个答案,知道以下内容就足够了:

  1. 函数是描述符。
  2. 方法的绑定行为(即方法如何知道要传递的self)是通过函数的__get__ 方法和内置描述符协议实现的。
  3. 当你把一个描述符foo放到一个类上时,访问描述符实际上调用了.__get__方法。(这实际上只是对语句2的概括)

换句话说:

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,它就会成功。

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。它的效率会降低,并且不会自省(并且可能会让您的代码阅读者感到困惑)。这仅用于教育目的。

【讨论】:

  • 感谢您的友好回答 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-07-21
相关资源
最近更新 更多