Сравнение PHP __get() с __get __() и __getattr __() в Python

В чем разница между __get__() и __getattr__() в Python? Я исхожу из фона PHP, где есть только __get(). Когда следует использовать какую функцию?

Я пытался понять это на некоторое время. Я вижу много таких вопросов, как этот, но спрашивает о различии между __getattr__() и __getattribute__().

Ответ 1

Здесь вы найдете подробную документацию для всех этих методов здесь.

Исходя из PHP, вы должны сначала познакомиться с объектной моделью Python. Это намного богаче, чем PHP, поэтому вы не должны пытаться сопоставить свои знания PHP 1:1 с Python. Если вы хотите разработать PHP, используйте PHP. Если вы хотите развиваться в Python, изучите Python.

Возвращаясь к исходному вопросу: __getattr__, вероятно, является функцией, которая выполняет ту же функцию, что и функция __get в PHP. __get__ в Python используется для реализации дескрипторов. Подробности о дескрипторах также можно найти в документации, упомянутой выше.

Ответ 2

Прежде всего, PHP имеет не эквивалент Python __get__() – даже не близко! То, что вы ищете, определенно __getattr__().

Я исхожу из фона PHP, где есть только __get__

PHP имеет магический метод, называемый __get(), который вызывается всякий раз, когда вы пытаетесь открыть свойство, которое не существует.

Краткий список неэквивалентов

Во-первых, оставьте некоторые вещи:

  • PHP не имеет эквивалент Python __get__()
  • PHP имеет не эквивалент Python __getattr__()
  • PHP не имеет эквивалент Python __getattribute__()
  • Python не имеет эквивалент PHP __get()

(И для всех методов setter соответственно.)

Вопреки предположению Ахима, __get() делает не делать то же, что и Python __getattr__()!
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 думать о вызовах методов совершенно разные.

PHP "осведомленность о вызовах"

  • PHP obj знает, который вы вызываете методом
    • вы можете явно обрабатывать вызовы несуществующих методов на объект, который вы вызываете
    • это потому, что модель выражения PHP очень несовместима (PHP 5.4 - это небольшой шаг вперед, хотя)
  • но Python obj делает не.

Это позволяет PHP иметь obj.does_not_exist для оценки до 3, но obj.does_not_exist() до 5.

Насколько мне известно, на Python это невозможно сделать. (Это позволило бы нам описать непоследовательность PHP как функцию.)

Таким образом, мы можем расширить наш "неэквивалентный" список одним номером пули:

  • 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.