使用以下示例脚本:

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)似乎可行,但这对我来说仍然很奇怪。

最佳答案

classmethodstaticmethod是描述符,它们都不是您所期望的,而不仅仅是staticmethod

当您访问A.one时,它将在A上创建一个绑定(bind)方法,然后将其设为B的属性,但是由于它绑定(bind)到A,因此即使您调用clsA参数也始终是B.one(这在两个Python 2上都是和Python 3;到处都是错误的)。

当您访问A.two时,它返回的是原始函数对象(除了防止通过staticmethodself进行的绑定(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()

但是,因为这将使Btwo函数的实例脱离绑定(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)魔术,并且您可以获得staticmethodclassmethodonetwo包装版本。

08-04 06:20
查看更多