Python:将静态方法分配给类变量会出现错误

问题描述 投票:0回答:1

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

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

python static-methods class-method
1个回答
8
投票

背景故事(描述符协议)

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.