hit*_*han 3 python static-methods class-method
我想在Python中为一个类变量赋一个静态方法,下面就是我的代码.
class Klass:
classVariable = None
@staticmethod
def method():
print "method called"
Klass.classVariable = Klass.method
Klass.method()
Klass.classVariable()
Run Code Online (Sandbox Code Playgroud)
这给了我最后一行的错误,
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
Run Code Online (Sandbox Code Playgroud)
当我做:
result = Foo.val
Run Code Online (Sandbox Code Playgroud)
Python实际上做了:
Foo.val.__get__(None, Foo)
Run Code Online (Sandbox Code Playgroud)
当我做:
f = Foo()
f.val
Run Code Online (Sandbox Code Playgroud)
python做:
f = Foo()
type(f).val.__get__(f, type(f))
Run Code Online (Sandbox Code Playgroud)
看起来(在python2.x上),staticmethod它的实现使得它的__get__方法返回一个常规函数.您可以通过打印以下类型来查看Klass.method:
print type(Klass.method) # <type 'function'>
Run Code Online (Sandbox Code Playgroud)
所以我们学到的是返回的方法Klass.method.__get__只是一个常规函数.
当你将那个常规函数放到一个类上时,它的__get__方法返回一个instancemethod(它需要一个self参数).这并不奇怪...我们一直这样做:
class Foo(object):
def bar(self):
print self
Run Code Online (Sandbox Code Playgroud)
与python没有什么不同:
def bar(self):
print self
class Foo(object):
pass
Foo.bar = bar
Run Code Online (Sandbox Code Playgroud)
除了第一个版本更容易阅读并且不会使您的模块名称空间混乱.
所以现在我们已经解释了你的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()
Run Code Online (Sandbox Code Playgroud)
@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()
Run Code Online (Sandbox Code Playgroud)
这里的诀窍是我__get__没有返回一个函数.它返回自己.当你把它放在一个不同的类(或同一个类)时,它__get__仍将只返回自己.因为它自己返回__get__,所以它需要伪装成一个函数(所以它可以在"__gotten__"之后调用)所以我实现了一个自定义__call__方法来做正确的事情(传递给委托函数并返回结果).
请注意,我并不是主张你用它StaticMethod来代替staticmethod.它的效率会降低,而且不会过于苛刻(并且可能会让您的代码读者感到困惑).这仅用于教育目的.