В чем разница между __get__() и __getattr__() в Python? Я исхожу из фона PHP, где есть только __get() . Когда следует использовать эту функцию?
Я пытался понять это на некоторое время. Я вижу много таких вопросов, как этот , спрашивая о различии между __getattr__() и __getattribute__() .
Здесь вы найдете подробную документацию по всем этим методам.
Исходя из PHP, вы должны сначала познакомиться с объектной моделью Python. Это намного богаче, чем PHP, поэтому вы не должны пытаться сопоставить свои знания PHP 1: 1 с Python. Если вы хотите разработать PHP, используйте PHP. Если вы хотите развиваться в Python, изучите Python.
Возвращаясь к исходному вопросу: __getattr__ , вероятно, является функцией, которая выполняет те же функции, что и функция __get в PHP. __get__ в Python используется для реализации дескрипторов. Подробности о дескрипторах также можно найти в документации, упомянутой выше.
Прежде всего, PHP не имеет эквивалента __get__() Python __get__() даже не закрывается! То, что вы ищете, определенно __getattr__() .
Я
__get__из фона PHP, где есть только__get__
PHP имеет магический метод __get() , который вызывается всякий раз, когда вы пытаетесь получить доступ к свойству, которого не существует.
Во-первых, давайте проясним некоторые вещи:
__get__() Python __getattr__() Python __getattr__() __getattribute__() __get() (И для всех методов сеттера соответственно.)
Вопреки предположению Ахима, __get() не делает то же самое, что и __getattr__() Python!
Python не различает методы и свойства, но PHP делает, поэтому PHP имеет второй метод: __call() .
__call() выполняется всякий раз, когда вы пытаетесь вызвать метод для объекта, который не существует. У Python нет эквивалента для этого, потому что метод – это просто объект (атрибут), который можно вызывать.
Пример в PHP:
<?php $obj = new stdClass(); ($obj->test)();
В Python это приведет к ошибке с AttributeError . Однако в PHP это даже не компилируется :
Ошибка анализа: синтаксическая ошибка, неожиданный '(' on line 4
Сравните это с Python:
obj.method() # is eqvuivalent to: (obj.method)()
Это важное различие. Мы пришли к выводу, что способ PHP и Python думать о вызовах методов совершенно другой.
obj знает, что вы вызываете метод
obj Python этого не делает. Это позволяет PHP иметь obj.does_not_exist для оценки до 3 , но obj.does_not_exist() до 5 .
Насколько мне известно, на Python это невозможно сделать. (Это позволило бы нам описать непоследовательность PHP как функцию.)
Таким образом, мы можем расширить наш «не эквивалентный» список одним номером пули:
__call() / __callStatic() PHP предоставляет два отдельных механизма:
__get() для несуществующих свойств __call() для вызовов несуществующих методов У Python есть только один механизм, поскольку он не различает свойства и методы, поскольку он касается всех атрибутов.
__getattr__() , когда атрибут не существует. obj.non_existing() не является специальным «синтаксисом вызова», это выражение, к которому применяется оператор вызова () : (obj.__getattr__("non_existing"))() Отказ от ответственности : __getattr__() не всегда вызывается, когда атрибут не существует. __getattribute__() принимает наивысший приоритет в цепочке поиска и поэтому может __getattr__() .
__get__() в Python – это нечто совершенно отличное от того, что было рассмотрено выше.
Документация описывает дескрипторы как "a descriptor is an object attribute with “binding behavior”" . Я могу сформировать интуитивное понимание того, что означает «привязывающее поведение», но только потому, что я уже понимаю, что делают дескрипторы.
Я бы решил описать дескрипторы как « самонаблюдаемые атрибуты», которые можно разделить на несколько классов.
Позвольте мне объяснить, что я имею в виду под «самосознанием». Такие атрибуты:
Эти атрибуты являются независимыми объектами: объектами «дескриптор», т.е. объектами, которые придерживаются так называемого «протокола дескриптора» , который определяет набор методов вместе с их соответствующей сигнатурой, которую могут реализовать такие объекты.
Объект не имеет атрибута дескриптора. Фактически, они принадлежат к соответствующему классу объекта (или его предку). Однако тот же объект дескриптора может «принадлежать» нескольким классам.
Примечание : «С кем они читаются», каков правильный способ ссылаться на obj в obj.attr ? Я бы сказал: attr обращается к «через» obj .