Python:将静态方法分配给 class 变量会出错
Python: Assigning staticmethod to class variable gives error
我想将静态方法分配给 Python 中的 class 变量,下面是我的代码。
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).
但是当我将静态方法更改为 class 方法时它起作用了。谁能告诉我为什么会这样?
背景故事(描述符协议)
首先,我们需要了解一下python descriptors...
对于这个答案,知道以下内容应该就足够了:
- 函数是描述符。
- 方法的绑定行为(即方法如何知道
self
传递什么)是通过函数的 __get__
方法和内置描述符协议实现的。
- 当你把描述符
foo
放在class上时,访问描述符实际上调用了.__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__
方法 return 是一个常规函数。您可以通过打印 Klass.method
:
的类型来查看
print type(Klass.method) # <type 'function'>
所以我们了解到 Klass.method.__get__
编辑的方法 return 只是一个常规函数。
当您将该常规函数放到 class 上时,它是 __get__
方法 return 和 instancemethod
(需要 self
参数)。这并不奇怪......我们一直这样做:
class Foo(object):
def bar(self):
print self
与 python 没有区别:
def bar(self):
print self
class Foo(object):
pass
Foo.bar = bar
除了第一个版本更容易阅读并且不会弄乱您的模块命名空间。
现在我们已经解释了您的静态方法是如何变成实例方法的。我们能做些什么吗?
解决方案
当你把方法放到 class 上时,再次将它指定为 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__
没有 return 函数。它 return 是 本身 。当你把它放在不同的 class(或相同的 class)上时,它的 __get__
仍然只是 return 本身。因为它是从 __get__
自身 return,它需要伪装成一个函数(所以它可以在“__gotten__”之后调用)所以我实现了一个自定义 __call__
方法做正确的事情(传递给委托函数和 return 结果)。
请注意,我并不是提倡您使用此 StaticMethod
而不是 staticmethod
。它的效率会降低并且不会自省(并且可能会让您的代码读者感到困惑)。这仅用于教育目的。
我想将静态方法分配给 Python 中的 class 变量,下面是我的代码。
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).
但是当我将静态方法更改为 class 方法时它起作用了。谁能告诉我为什么会这样?
背景故事(描述符协议)
首先,我们需要了解一下python descriptors...
对于这个答案,知道以下内容应该就足够了:
- 函数是描述符。
- 方法的绑定行为(即方法如何知道
self
传递什么)是通过函数的__get__
方法和内置描述符协议实现的。 - 当你把描述符
foo
放在class上时,访问描述符实际上调用了.__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__
方法 return 是一个常规函数。您可以通过打印 Klass.method
:
print type(Klass.method) # <type 'function'>
所以我们了解到 Klass.method.__get__
编辑的方法 return 只是一个常规函数。
当您将该常规函数放到 class 上时,它是 __get__
方法 return 和 instancemethod
(需要 self
参数)。这并不奇怪......我们一直这样做:
class Foo(object):
def bar(self):
print self
与 python 没有区别:
def bar(self):
print self
class Foo(object):
pass
Foo.bar = bar
除了第一个版本更容易阅读并且不会弄乱您的模块命名空间。
现在我们已经解释了您的静态方法是如何变成实例方法的。我们能做些什么吗?
解决方案
当你把方法放到 class 上时,再次将它指定为 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__
没有 return 函数。它 return 是 本身 。当你把它放在不同的 class(或相同的 class)上时,它的 __get__
仍然只是 return 本身。因为它是从 __get__
自身 return,它需要伪装成一个函数(所以它可以在“__gotten__”之后调用)所以我实现了一个自定义 __call__
方法做正确的事情(传递给委托函数和 return 结果)。
请注意,我并不是提倡您使用此 StaticMethod
而不是 staticmethod
。它的效率会降低并且不会自省(并且可能会让您的代码读者感到困惑)。这仅用于教育目的。