В чем разница между __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
.