我想在 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 描述符...
对于这个答案,了解以下内容就足够了:
self
)是通过函数的 __get__
方法和内置描述符协议实现的。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
。它会降低效率并且不那么内省(并且可能会让您的代码读者感到困惑)。这仅用于教育目的。