为什么 @staticmethod 没有在 类 中保留,而 @classmethod 是?
Why is @staticmethod not preserved across classes, when @classmethod is?
采用以下示例脚本:
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 装饰器似乎在 classes 中得到了保留,但 @staticmethod 却没有。
Python 3.4 表现符合预期:
I am class
I am static
为什么 Python2 不保留 @staticmethod,有解决方法吗?
编辑:从 class 中取出两个(并保留 @staticmethod)似乎可行,但这对我来说仍然很奇怪。
DISCLAIMER: This is not really an answer, but it doesn't fit into a comment format either.
请注意 Python2 @classmethod
也不是 正确地 在 class 中保留。在下面的代码中,对 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')
classmethod
和 staticmethod
是描述符,它们都没有按照您的预期进行,而不仅仅是 staticmethod
.
当您访问 A.one
时,它会在 A
上创建绑定方法,然后将其设为 B
的属性,但因为它绑定到 A
,所以cls
参数将始终为 A
,即使您调用 B.one
(Python 2 和 Python 3 都是这种情况;到处都是错误的)。
当您访问 A.two
时,它返回原始函数对象(staticmethod
描述符不需要做任何特殊的事情,除了防止绑定传递 self
或 cls
,所以它只是 returns 它包装的内容)。但是那个原始函数对象然后作为未绑定的实例方法附加到 B
,因为没有 staticmethod
包装,它就像你正常定义它一样。
后者在Python 3中起作用的原因是Python 3没有未绑定方法的概念。它具有函数(如果通过 class 的实例访问则成为绑定方法)和绑定方法,其中 Python 2 具有函数、未绑定方法和绑定方法。
未绑定的方法检查它们是否使用正确类型的对象调用,因此是您的错误。普通函数只需要正确数量的参数。
Python 3 中的 staticmethod
装饰器仍然返回原始函数对象,但在 Python 3 中,这很好;因为它不是一个特殊的未绑定方法对象,所以如果您在 class 本身上调用它,它就像一个命名空间函数,而不是任何类型的方法。如果您尝试这样做,您会看到问题:
B().two()
不过,因为这会从 B
和 two
函数的实例中创建一个绑定方法,传递一个额外的参数 (self
) two
不接受。基本上,在 Python 3 上,staticmethod
可以方便地让您在实例上调用函数而不会导致绑定,但是如果您只通过引用 class 本身来调用函数,则不是需要,因为它只是一个普通函数,而不是 Python 2 "unbound method".
如果您出于某种原因执行此复制(通常,我建议从 A
继承,但无论如何),并且您希望确保获得函数的描述符包装版本,而不是无论在 A
上访问时描述符给你什么,你都可以通过直接访问 A
的 __dict__
:
来绕过描述符协议
class B(object):
one = A.__dict__['one']
two = A.__dict__['two']
通过直接从 class 字典中复制,永远不会调用描述符协议魔法,你会得到 one
和 staticmethod
和 classmethod
包装版本=30=].
采用以下示例脚本:
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 装饰器似乎在 classes 中得到了保留,但 @staticmethod 却没有。
Python 3.4 表现符合预期:
I am class
I am static
为什么 Python2 不保留 @staticmethod,有解决方法吗?
编辑:从 class 中取出两个(并保留 @staticmethod)似乎可行,但这对我来说仍然很奇怪。
DISCLAIMER: This is not really an answer, but it doesn't fit into a comment format either.
请注意 Python2 @classmethod
也不是 正确地 在 class 中保留。在下面的代码中,对 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')
classmethod
和 staticmethod
是描述符,它们都没有按照您的预期进行,而不仅仅是 staticmethod
.
当您访问 A.one
时,它会在 A
上创建绑定方法,然后将其设为 B
的属性,但因为它绑定到 A
,所以cls
参数将始终为 A
,即使您调用 B.one
(Python 2 和 Python 3 都是这种情况;到处都是错误的)。
当您访问 A.two
时,它返回原始函数对象(staticmethod
描述符不需要做任何特殊的事情,除了防止绑定传递 self
或 cls
,所以它只是 returns 它包装的内容)。但是那个原始函数对象然后作为未绑定的实例方法附加到 B
,因为没有 staticmethod
包装,它就像你正常定义它一样。
后者在Python 3中起作用的原因是Python 3没有未绑定方法的概念。它具有函数(如果通过 class 的实例访问则成为绑定方法)和绑定方法,其中 Python 2 具有函数、未绑定方法和绑定方法。
未绑定的方法检查它们是否使用正确类型的对象调用,因此是您的错误。普通函数只需要正确数量的参数。
Python 3 中的 staticmethod
装饰器仍然返回原始函数对象,但在 Python 3 中,这很好;因为它不是一个特殊的未绑定方法对象,所以如果您在 class 本身上调用它,它就像一个命名空间函数,而不是任何类型的方法。如果您尝试这样做,您会看到问题:
B().two()
不过,因为这会从 B
和 two
函数的实例中创建一个绑定方法,传递一个额外的参数 (self
) two
不接受。基本上,在 Python 3 上,staticmethod
可以方便地让您在实例上调用函数而不会导致绑定,但是如果您只通过引用 class 本身来调用函数,则不是需要,因为它只是一个普通函数,而不是 Python 2 "unbound method".
如果您出于某种原因执行此复制(通常,我建议从 A
继承,但无论如何),并且您希望确保获得函数的描述符包装版本,而不是无论在 A
上访问时描述符给你什么,你都可以通过直接访问 A
的 __dict__
:
class B(object):
one = A.__dict__['one']
two = A.__dict__['two']
通过直接从 class 字典中复制,永远不会调用描述符协议魔法,你会得到 one
和 staticmethod
和 classmethod
包装版本=30=].