Ответ 1
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 имеет функции, несвязанные методы и связанные методы.
Несвязанные методы проверяют, что они вызываются с объектом правильного типа, таким образом, ваша ошибка. Обычные функции просто требуют правильного количества аргументов.
Декоратор staticmethod
в Python 3 по-прежнему возвращает исходный объект функции, но в Python 3 это прекрасно; поскольку он не является специальным объектом unbound method, если вы вызываете его в самом классе, он просто как функция с именами, а не какой-либо метод. Если вы попытаетесь сделать это, вы увидите проблему:
B().two()
потому что это сделает связанный метод из этого экземпляра B
и функции two
, передав дополнительный аргумент (self
), который two
не принимает. В принципе, на Python 3, staticmethod
- это удобство, позволяющее вам вызывать функцию на экземплярах без необходимости связывания, но если вы только когда-либо вызываете функцию, ссылаясь на сам класс, это не нужно, потому что это просто простая функция, а не Python 2 "unbound method".
Если у вас есть причина для выполнения этой копии (как правило, я предлагаю наследовать от A
, но что бы то ни было), и вы хотите удостовериться, что вы получили дескриптор завернутой версии функции, а не то, что дескриптор дает вам при доступе к A
вы обходите протокол дескриптора, напрямую обращаясь к A
__dict__
:
class B(object):
one = A.__dict__['one']
two = A.__dict__['two']
Прямым копированием из словаря класса, магия протокола дескриптора никогда не вызывается, и вы получаете завершенные версии staticmethod
и classmethod
one
и two
.