Чтобы понять, почему вы получаете предупреждение, которое вы получаете, вам нужно немного разбираться в дескрипторах и .
Декораторы
Декоратор - это вызываемый объект, который заменяет объект, который он украшает, и присваивает ему то же имя в пространстве имен. Обычно декораторы используются для функций и классов для добавления некоторой функциональности, такой как проверка типов или многопоточность, или что-то в этом роде, но на самом деле они могут возвращать что угодно.
Поскольку выходные данные декоратора не обязательно должны быть того же типа, что и входные данные, или выполнять какую-либо одну и ту же обработку, порядок декораторов очень важен. Декораторы применяются по порядку от ближайшего к функции до верхнего в списке. В вашем случае abstractmethod
, затем staticmethod
, затем property
.
дескрипторы
Дескрипторы определяют довольно сложный протокол, который позволяет настраивать объекты с использованием поведения привязки, которое они обеспечивают. Для ваших целей вам нужно знать, что функции являются дескрипторами, и что размещение их в объекте класса использует это. Когда вы вызываете любой дескриптор, который определен в классе в экземпляре этого класса, протокол дескриптора связывает дескриптор с экземпляром, используя метод дескриптора __get__
. Сам дескриптор даже не должен быть вызываемым, и также не является возвращаемым значением __get__
, хотя в большинстве случаев ожидается, что оно будет. Для функции __get__
возвращает замыкание, которое автоматически передает self
в качестве первого позиционного аргумента.
Например, если дан класс A
с методом def b(self, arg):
и экземпляр этого класса с именем a
, выполнение a.b(arg)
превращается в A.b.__get__(a, A)(arg)
. Таким образом, хотя b
определено с двумя позиционными аргументами, ему требуется только один, чтобы явно передаваться при вызове экземпляра. Однако, когда вы вызываете b
через класс, например, A.b(a, arg)
, это обычная функция, и вам нужно передать все параметры вручную, включая self
.
Собираем все вместе
abstractmethod
, property
и staticmethod
- все декораторы, которые возвращают вызываемые объекты дескриптора. Однако метод их дескрипторов __get__
работает немного иначе, чем __get__
обычного функционального объекта.
abstractmethod
создает довольно обычный метод класса, но он взаимодействует с метаклассом ABCMeta
, так что вы получаете все виды полезных ошибок при попытке создать экземпляр класса с абстрактными методами. Он никак не изменяет входные параметры, ожидаемые результатом. Фактически, документация намекает на то, что все побочные эффекты могут быть связаны с метаклассом, и что исходный ввод просто проходит. Единственное, что нужно иметь в виду, это
Когда abstractmethod()
применяется в сочетании с другими дескрипторами метода, его следует применять как самый внутренний декоратор, как показано в следующих примерах использования: ... *
Ваш код, похоже, следует этому запрету. На самом деле abstractmethod
не имеет ничего общего с вашим предупреждением, но в любом случае было бы неплохо упомянуть его здесь.
staticmethod
возвращает вызываемый объект, который обходит обычное поведение привязки, чтобы создать метод, которому не важно, из какого класса или экземпляра он вызывается. В частности, метод, связанный с использованием staticmethod.__get__
, передаст свои аргументы в вашу функцию, вместо того, чтобы предварительно добавлять self
(т. Е. __get__
в основном просто возвращает вашу исходную функцию). Вы можете себе представить, что это будет проблемой для чего-то, что ожидает получения параметра self
, например, для установщика свойства.
В отличие от abstractmethod
и staticmethod
, property
создает дескриптор данных. Это означает, что он возвращает объект, имеющий привязку __get__
и привязку __set__
(а также привязку __del__
). Метод __get__
свойства работает почти так же, как метод __get__
обычной функции, но применяется специально к функции-получателю. property
очень волнует, из какого экземпляра он вызывается, потому что, конечно, вы хотите, чтобы разные экземпляры имели разные значения атрибута, который переносит свойство.
Итак, в вашем коде есть staticmethod
, за которым следует property
. Первый декоратор возвращает функцию, которая не добавляет self
к своему списку аргументов при привязке, в то время как вторая ожидает такую, которая это делает. Ничто не мешает вам вызывать декораторы, но предупреждение IDE говорит вам, что вам не удастся вызвать результирующий объект. Если вы попытаетесь получить доступ к my_property
в конкретной реализации AnyAbstractClass
, вы, вероятно, получите TypeError
, сообщающий вам, что my_property
не принимает никаких позиционных параметров, но он был задан, потому что property.__get__
будет предшествовать self
в список аргументов статического метода, который не принимает никаких аргументов.
Имейте в виду, что применение staticmethod
к результату property
также не поможет вам. Экземпляр property
вообще не вызывается. Он работает полностью через свои __get__
, __set__
и __del__
методы, в то время как staticmethod
предполагает, что вы передаете вызываемый.
Решение
Как вы правильно обнаружили, staticmethod
и property
плохо смешиваются. Свойство, по самой своей природе, должно всегда знать о экземпляре, на котором оно работает. Правильный способ сделать это - добавить параметр self
и разрешить нормальную привязку метода.
И property
, и staticmethod
хорошо работают с abstractmethod
(если сначала применяется abstractmethod
), потому что он фактически не меняет исходную функцию. На самом деле в документах abstractmethod
конкретно упоминается, что метод получения, установки или удаления абстрактного property
делает все свойство абстрактным.
TL; DR
staticmethod
возвращает дескриптор, который можно вызвать, но чей метод __get__
возвращает несвязанную версию самого себя. property
создает не вызываемый дескриптор, чей метод __get__
вызывает метод получения свойства. Использование результирующего свойства попытается передать self
статическому методу, который его не принимает.