23

Рассмотрим следующий пример сценария:Почему @staticmethod не сохраняется в классах, когда @classmethod?

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), похоже, работает, но это все еще кажется странным для меня.

ответ

16

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 имеет функции, несвязанные методы и связанные методы.

Unbound methods проверяет, что они вызываются с объектом соответствующего типа, таким образом, ваша ошибка. Обычные функции просто требуют правильного количества аргументов.

Декоратор staticmethod в Python 3 все еще возвращает исходный объект функции, но в Python 3 это нормально; так как это не специальный объект unbound method, если вы вызываете его на самом классе, это просто как функция с именами, а не какой-либо метод.Вы бы увидели проблему, если вы пытались сделать:

B().two() 

, хотя, потому что это сделает связанный метод из этого экземпляра B и функции two, передавая дополнительный аргумент (self), что two не делает принимаем. В принципе, на Python 3, staticmethod - это удобство, позволяющая вам вызывать функцию на экземплярах без необходимости связывания, но если вы только когда-либо вызываете функцию, ссылаясь на сам класс, это не нужно, потому что это просто простая функция, а не Python 2 "несвязанный метод".

Если у вас есть причина для выполнения этой копии (как правило, я предлагаю наследовать от A, но что бы то ни было), и вы хотите удостовериться, что вы получили дескриптор завернутой версии функции, а не то, что дескриптор дает вам при доступе на A, вы бы обойти протокол дескриптора путем прямого доступа к A «ы __dict__:

class B(object): 
    one = A.__dict__['one'] 
    two = A.__dict__['two'] 

Прямого копирования из словаря класса, магический протокол дескриптора никогда не вызываются, и вы получите staticmethod и classmethod завернутые версии one и two.

+0

Большое спасибо за информативный ответ! Вариант использования - это тестовый набор: https://github.com/al4/python-checkrunner/blob/master/tests/test_checkrunner.py. Я хотел повторно использовать функции и сгруппировать их в класс, чтобы не допустить, чтобы PyCharm жаловался на применение декораторы вне класса. Я думаю, лучший подход - это вручную вызвать staticmethod с классом «sub». –

+1

@AlexForbes: В этом случае я бы реализовал класс общих функций и использовал его как mixin в классах, которые в них нуждаются. 'class CommonTests (object): ... define one, two ...', а затем смешивать его через множественное наследование: 'class A (unittests.TestCase, CommonTests): ... Специальные тесты ...', затем 'class B (unittests.TestCase, CommonTests): ... B конкретные тесты ...'. Теперь все методы CommonTests автоматически наследуются в 'A' и' B' без необходимости назначать их по имени индивидуально. Класс mixin не наследуется от 'TestCase', поэтому он не будет протестирован сам по себе. – ShadowRanger

4

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: На самом деле это не ответ, но он не соответствует формату комментария.

Обратите внимание, что с python2 @classmethod НЕ правильно сохраняется по классам либо. В приведенном ниже коде, вызов B.one() работ, как если бы он был вызван через класс 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') 

 Смежные вопросы

  • Нет связанных вопросов^_^