我想在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).
但是当我将静态方法更改为类方法时,它可以工作。任何人都可以告诉我为什么会这样吗?
答案 0 :(得分:6)
首先,我们需要了解一下python descriptors ...
对于这个答案,应该知道以下内容:
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如何变成一个实例方法。我们能做些什么吗?
当你把这个方法放到课堂上时,再次将它指定为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
。它的效率会降低,而且不会过于苛刻(并且可能会让您的代码读者感到困惑)。这仅用于教育目的。