使用以下示例脚本:
class A(object):
@classmethod
def one(cls):
print("I am class")
@staticmethod
def two():
print("I am static")
class B(object):
one = A.one
two = A.two
B.one()
B.two()
当我使用Python 2.7.11运行此脚本时,我得到:
I am class
Traceback (most recent call last):
File "test.py", line 17, in <module>
B.two()
TypeError: unbound method two() must be called with B instance as first argument (got nothing instead)
看来@classmethod装饰器在所有类中都保留了,但@staticmethod却没有。
Python 3.4的行为符合预期:
I am class
I am static
为什么Python2不保留@staticmethod,并且有解决方法?
编辑:从一个类中取出两个(并保留@staticmethod)似乎可行,但这对我来说仍然很奇怪。
最佳答案
classmethod
和staticmethod
是描述符,它们都不是您所期望的,而不仅仅是staticmethod
。
当您访问A.one
时,它将在A
上创建一个绑定(bind)方法,然后将其设为B
的属性,但是由于它绑定(bind)到A
,因此即使您调用cls
,A
参数也始终是B.one
(这在两个Python 2上都是和Python 3;到处都是错误的)。
当您访问A.two
时,它返回的是原始函数对象(除了防止通过staticmethod
或self
进行的绑定(bind)之外,cls
描述符不需要执行任何特殊操作,因此它只返回包装的内容)。但是该原始函数对象然后作为未绑定(bind)实例方法附加到B
上,因为没有staticmethod
换行,就如同您正常定义它一样。
后者在Python 3中起作用的原因是Python 3没有未绑定(bind)方法的概念。它具有函数(如果通过类的实例访问,则成为绑定(bind)方法)和绑定(bind)方法,其中Python 2具有函数,非绑定(bind)方法和绑定(bind)方法。
未绑定(bind)方法检查是否已使用正确类型的对象调用了它们,因此会出错。普通函数只需要正确数量的参数。
Python 3中的staticmethod
装饰器仍在返回原始函数对象,但是在Python 3中,这很好。因为它不是一个特殊的未绑定(bind)方法对象,所以如果您在类本身上调用它,则它就像一个命名空间函数,而不是任何类型的方法。如果您尝试这样做,就会看到问题:
B().two()
但是,因为这将使
B
和two
函数的实例脱离绑定(bind)方法,并传递self
不接受的额外参数(two
)。基本上,在Python 3上,staticmethod
是一种方便的方法,可让您在实例上调用该函数而不引起绑定(bind),但是如果您只通过引用该类本身来调用该函数,则不需要它,因为它只是一个普通函数,而不是Python 2“未绑定(bind)方法”。如果您出于某种原因执行此复制(通常,我建议从
A
继承,但无论如何),并且要确保您获得函数的描述符包装版本,而不是在A
上访问时描述符提供的内容,您可以通过直接访问A
的__dict__
来绕过描述符协议(protocol):class B(object):
one = A.__dict__['one']
two = A.__dict__['two']
通过直接从类字典中进行复制,永远不会调用描述符协议(protocol)魔术,并且您可以获得
staticmethod
和classmethod
的one
和two
包装版本。