Ale*_*bes 23 python static-methods class-method python-2.7 python-descriptors
采用以下示例脚本:
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()
Run Code Online (Sandbox Code Playgroud)
当我使用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)
Run Code Online (Sandbox Code Playgroud)
似乎@classmethod装饰器在类中保留,但@staticmethod不是.
Python 3.4的行为符合预期:
I am class
I am static
Run Code Online (Sandbox Code Playgroud)
为什么Python2不能保留@staticmethod,是否有解决方法?
编辑:从课堂上取两个(并保留@staticmethod)似乎有效,但这对我来说似乎仍然很奇怪.
Sha*_*ger 15
classmethod并且staticmethod是描述符,并且他们都没有做你期望的事情,而不仅仅是staticmethod.
当你访问时A.one,它正在创建一个绑定方法A,然后使它成为一个属性B,但因为它被绑定A,cls参数将始终是A,即使你调用B.one(这是Python 2和Python 3的情况;它在各处都是错的).
当您访问A.two,它返回原始函数对象(staticmethod描述并不需要从预防的结合,将通过做什么特别的一边self或者cls,所以它只是返回它所包裹).但是那个原始函数对象然后被附加到B一个未绑定的实例方法,因为没有staticmethod包装,它就像你正常定义它一样.
后者在Python 3中工作的原因是Python 3没有未绑定方法的概念.它具有函数(如果通过类的实例访问,则成为绑定方法)和绑定方法,其中Python 2具有函数,未绑定方法和绑定方法.
未绑定方法检查是否使用正确类型的对象调用它们,从而检查错误.普通函数只需要正确数量的参数.
staticmethodPython 3中的装饰器仍然返回原始函数对象,但在Python 3中,这很好; 因为它不是一个特殊的未绑定方法对象,如果你在类本身上调用它,它就像一个命名空间函数,而不是任何类型的方法.如果您尝试这样做,您会看到问题:
B().two()
Run Code Online (Sandbox Code Playgroud)
但是,因为那将从该实例B和two函数中生成一个绑定方法,传递一个不接受的额外参数(self)two.基本上,在Python 3上,staticmethod让你在不引起绑定的情况下调用实例上的函数是一种方便,但是如果你只是通过引用类本身来调用函数,则不需要它,因为它只是一个简单的函数,而不是Python 2 "无约束的方法".
如果你有理由执行这个副本(通常,我建议继承A,但无论如何),并且你想确保获得函数的描述符包装版本,而不是描述符在访问时给你的任何内容A,你通过直接访问A's来绕过描述符协议__dict__:
class B(object):
one = A.__dict__['one']
two = A.__dict__['two']
Run Code Online (Sandbox Code Playgroud)
通过从类字典直接复制,描述协议魔法永远不会调用,你得到的staticmethod与classmethod包裹版本one和two.
免责声明:这并不是真正的答案,但它也不适合评论格式。
请注意,Python2也@classmethod不能跨类正确保存。在下面的代码中,对 的调用B.one()就像通过 class 调用一样A:
$ cat test.py
class A(object):
@classmethod
def one(cls):
print("I am class", cls.__name__)
class A2(A):
pass
class B(object):
one = A.one
A.one()
A2.one()
B.one()
$ python2 test.py
('I am class', 'A')
('I am class', 'A2')
('I am class', 'A')
Run Code Online (Sandbox Code Playgroud)