背景故事(描述符协议)
首先,我们需要了解一点pythondescriptors...
对于这个答案,知道以下内容就足够了:
- 函数是描述符。
- 方法的绑定行为(即方法如何知道要传递的
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。它的效率会降低,并且不会自省(并且可能会让您的代码阅读者感到困惑)。这仅用于教育目的。