getattr python что это

Пользовательские атрибуты в Python

__dict__

В примере описан класс StuffHolder с одним атрибутом stuff, который, наследуют оба его экземпляра. Добавление объекту b атрибута b_stuff, никак не отражается на a.
Посмотрим на __dict__ всех действующих лиц:

(У класса StuffHolder в __dict__ хранится объект класса dict_proxy с кучей разного барахла, на которое пока не нужно обращать внимание).

Ни у a ни у b в __dict__ нет атрибута stuff, не найдя его там, механизм поиска ищет его в __dict__ класса (StuffHolder), успешно находит и возвращает значение, присвоенное ему в классе. Ссылка на класс хранится в атрибуте __class__ объекта.
Поиск атрибута происходит во время выполнения, так что даже после создания экземпляров, все изменения в __dict__ класса отразятся в них:

В случае присваивания значения атрибуту экземпляра, изменяется только __dict__ экземпляра, то есть значение в __dict__ класса остаётся неизменным (в случае, если значением атрибута класса не является data descriptor):

Если имена атрибутов в классе и экземпляре совпадают, интерпретатор при поиске значения выдаст значение экземпляра (в случае, если значением атрибута класса не является data descriptor):

По большому счёту это всё, что можно сказать про __dict__. Это хранилище атрибутов, определённых пользователем. Поиск в нём производится во время выполнения и при поиске учитывается __dict__ класса объекта и базовых классов. Также важно знать, что есть несколько способов переопределить это поведение. Одним из них является великий и могучий Дескриптор!

Дескрипторы

С простыми типами в качестве значений атрибутов пока всё ясно. Посмотрим, как ведёт себя функция в тех же условиях:

WTF!? Спросите вы… возможно. Я бы спросил. Чем функция в этом случае отличается от того, что мы уже видели? Ответ прост: методом __get__.

Этот метод переопределяет механизм получения значения атрибута func экземпляра fh, а объект, который реализует этот метод непереводимо называется non-data descriptor.

Дескриптор — это объект, доступ к которому через атрибут переопределён методами в дескриптор протоколе:

Дескрипторы данных

Рассмотрим повнимательней дескриптор данных:

Стоит обратить внимание, что вызов DataHolder.data передаёт в метод __get__ None вместо экземпляра класса.
Проверим утверждение о том, что у дата дескрипторов преимущество перед записями в __dict__ экземпляра:

Так и есть, запись в __dict__ экземпляра игнорируется, если в __dict__ класса экземпляра (или его базового класса) существует запись с тем же именем и значением — дескриптором данных.

Ещё один важный момент. Если изменить значение атрибута с дескриптором через класс, никаких методов дескриптора вызвано не будет, значение изменится в __dict__ класса как если бы это был обычный атрибут:

Дескрипторы не данных

Пример дескриптора не данных:

Его поведение слегка отличается от того, что вытворял дата-дескриптор. При попытке присвоить значение атрибуту non_data, оно записалось в __dict__ экземпляра, скрыв таким образом дескриптор, который хранится в __dict__ класса.

Примеры использования

Дескрипторы это мощный инструмент, позволяющий контролировать доступ к атрибутам экземпляра класса. Один из примеров их использования — функции, при вызове через экземпляр они становятся методами (см. пример выше). Также распространённый способ применения дескрипторов — создание свойства (property). Под свойством я подразумеваю некое значение, характеризующее состояние объекта, доступ к которому управляется с помощью специальных методов (геттеров, сеттеров). Создать свойство просто с помощью дескриптора:

Или можно воспользоваться встроенным классом property, он представляет собой дескриптор данных. Код, представленный выше можно переписать следующим образом:

В обоих случаях мы получим одинаковое поведение:

Важно знать, что property всегда является дескриптором данных. Если в его конструктор не передать какую либо из функций (геттер, сеттер или делитер), при попытке выполнить над атрибутом соответствующее действие — выкинется AttributeError.

__getattr__(), __setattr__(), __delattr__() и __getattribute__()

Если нужно определить поведение какого-либо объекта как атрибута, следует использовать дескрипторы (например property). Тоже справедливо для семейства объектов (например функций). Ещё один способ повлиять на доступ к атрибутам: методы __getattr__(), __setattr__(), __delattr__() и __getattribute__(). В отличие от дескрипторов их следует определять для объекта, содержащего атрибуты и вызываются они при доступе к любому атрибуту этого объекта.

__getattr__(self, name) будет вызван в случае, если запрашиваемый атрибут не найден обычным механизмом (в __dict__ экземпляра, класса и т.д.):

__getattribute__(self, name) будет вызван при попытке получить значение атрибута. Если этот метод переопределён, стандартный механизм поиска значения атрибута не будет задействован. Следует иметь ввиду, что вызов специальных методов (например __len__(), __str__()) через встроенные функции или неявный вызов через синтаксис языка осуществляется в обход __getattribute__().

__setattr__(self, name, value) будет вызван при попытке установить значение атрибута экземпляра. Аналогично __getattribute__(), если этот метод переопределён, стандартный механизм установки значения не будет задействован:

Читайте также:  что делать если в помещении завелись блохи

__delattr__(self, name) — аналогичен __setattr__(), но используется при удалении атрибута.

При переопределении __getattribute__(), __setattr__() и __delattr__() следует иметь ввиду, что стандартный способ получения доступа к атрибутам можно вызвать через object:

__slots__

… Я боялся что изменения в системе классов плохо повлияют на производительность. В частности, чтобы дескрипторы данных работали корректно, все манипуляции атрибутами объекта начинались с проверки __dict__ класса на то, что этот атрибут является дескриптором данных…

На случай, если пользователи разочаруются ухудшением производительности, заботливые разработчики python придумали __slots__.
Наличие __slots__ ограничивает возможные имена атрибутов объекта теми, которые там указаны. Также, так как все имена атрибутов теперь заранее известны, снимает необходимость создавать __dict__ экземпляра.

Оказалось, что опасения Guido не оправдались, но к тому времени, как это стало ясно, было уже слишком поздно. К тому же, использование __slots__ действительно может увеличить производительность, особенно уменьшив количество используемой памяти при создании множества небольших объектов.

Заключение

Доступ к атрибутом в python можно контролировать огромным количеством способов. Каждый из них решает свою задачу, а вместе они подходят практически под любой мыслимый сценарий использования объекта. Эти механизмы — основа гибкости языка, наряду с множественным наследованием, метаклассами и прочими вкусностями. У меня ушло некоторое время на то, чтобы разобраться, понять и, главное, принять это множество вариантов работы атрибутов. На первый взгляд оно показалось слегка избыточным и не особенно логичным, но, учитывая, что в ежедневном программировании это редко пригодиться, приятно иметь в своём арсенале такие мощные инструменты.
Надеюсь, и вам эта статья прояснила парочку моментов, до которых руки не доходили разобраться. И теперь, с огнём в глазах и уверенностью в Точке, вы напишите огромное количество наичистейшего, читаемого и устойчивого к изменениям требований кода! Ну или комментарий.

Источник

Функция getattr() в Python

Синтаксис

Прежде чем перейти к реализации функции getattr(), сначала обсудим ее синтаксис. Синтаксис функции getattr() следующий:

Функция getattr() принимает в качестве аргумента три параметра:

Если атрибут name не найден и мы не определяем default_value, функция getattar() вызывает исключение AttributeError.

Примеры

Теперь мы рассмотрим несколько примеров с использованием функции getattr(). Мы создали студенческий класс и определили некоторые атрибуты для этого класса. Мы получаем доступ к этим атрибутам с помощью функции getattar().

Функция getattr() успешно вернула значения атрибутов.

Теперь мы попытаемся получить доступ к атрибуту, который не определен. Мы определим значение по умолчанию для этого отсутствующего атрибута.

Определенное значение – “Больше 20”.

В случае отсутствия атрибута “age” печатается определенное значение.

Если значение не определено, функция getattr() вызовет исключение AttributeError. Давайте посмотрим на пример этого.

Вы также можете использовать функцию getattr() с namedtuple для доступа к значениям. Namedtuple – это контейнер типа словаря, но вы можете получить доступ к его значениям с помощью функции getattar(). Namedtuple – это класс модуля коллекций. Давайте реализуем именованный кортеж и получим доступ к значениям с помощью функции getattr(). Мы создаем именованный набор для student.

Заключение

Функция getattar() – это встроенная функция в Python, которая возвращает значение атрибута объекта. Основная причина использования функции getattr() заключается в том, что она упрощает доступ к значению атрибута, используя имя атрибута. Имя атрибута определяется как строка, а значение мы получаем с помощью функции getattar(). Также можно определить значение для отсутствующего атрибута. Надеюсь, после прочтения этой статьи вы узнали больше о том, как использовать функцию getattar() в Python.

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Источник

Магические методы Python и __getattr__

Что общего у всех этих методов? Очевидно, что все они начинаются и заканчиваются двойными подчеркиваниями (__). Но помимо этого, что делает их «магическими методами», это то, что они вызываются как-то «специфически». Эти методы не вызываются вручную, Python это делает за нас. Например, при выполнении str(obj), выполняется вызов obj.__ str __ ().

Есть множество магических методов, но в этой статье будет рассмотрены __getattr__ и __getattribute__.

__getattr__

Этот метод позволит вам «улавливать» ссылки на атрибуты, которые не существуют в объекте. Рассмотрим простой пример, чтобы понять, как это работает:

В этом примере доступ к атрибуту завершается с ошибкой (вызов исключения AttributeError), поскольку атрибут does_not_exist не существует.

Читайте также:  с каким процентом поражения легких госпитализируют при коронавирусе в москве

Используя магический метод __getattr__можно перехватить поиск несуществующего атрибута и выполнить некоторое действие:

Но если атрибут существует, то __getattr__ вызываться не будет:

__getattribute__

__getattribute__ похож на __getattr__, с важным отличием, что __getattribute__ будет ВСЕГДА перехватывать поиск атрибута и не имеет значения, существует ли атрибут или нет. Рассмотрим простой пример:

В этом примере объект d уже содержит атрибут value. Но когда запрашивается доступ к нему, мы не получаем первоначальное ожидаемое значение («Python»), а вместо него получаем возвращаемое __getattribute__. Это означает, что был фактически потерян атрибут value, т. е. он стал «недостижимым».

Если вам когда-либо понадобится использовать __getattribute__ для моделирования чего-то похожего на __getattr__, нужно сделать ещё более сложную обработку Python:

Более реалистичный пример

__getattr__ позволяет определить поведение экземпляра пользовательского типа при попытке получения значения атрибута. Хорошим примером может быть расширение базового кортежа Python, чтобы добавить к нему немого синтаксического сахара:

К элементам обращаемся по-другому:

Каждый элемент в кортеже получает доступ как атрибут, причем первым элементом является атрибут _1, второй _2 и т. д.

Можно легко расширить общий кортеж Python, чтобы соответствовать этому поведению, для этого напишем очень простой код:

Результаты выполнения скрипта:

Вывод

Источник

Функция getattr() в Python

Функция getattr() в Python используется для получения значения атрибута объекта, и если атрибут этого объекта не найден, возвращается значение по умолчанию.

По сути, возврат значения по умолчанию – основная причина, по которой вам может потребоваться использовать функцию. Итак, прежде чем начать обучение, давайте посмотрим на основной синтаксис:

Пример

В этом разделе мы узнаем, как получить доступ к значениям атрибутов объекта с помощью функции getattr(). Предположим, мы пишем класс с именем Student. Основные атрибуты класса Student – это student_id и student_name. Теперь мы создадим объект класса Student и получим доступ к его атрибуту.

Итак, результат будет таким:

Значение по умолчанию

Если вы хотите получить доступ к любому атрибуту, который не принадлежит объекту, вы можете использовать параметр значения по умолчанию getattr().

Например, если для студента отсутствует атрибут student_cgpa, будет отображаться значение по умолчанию.

В следующем примере мы увидим пример. Мы также узнаем, что происходит, если атрибут отсутствует и мы не используем параметр значения по умолчанию.

Итак, после запуска кода вы получите такой результат:

Обратите внимание, что AttributeError возникает, когда значение по умолчанию не предоставляется при вызове функции getattr().

Причина использования функции

Основная причина использования getattr() заключается в том, что мы можем получить значение, используя имя атрибута, как String. Таким образом, вы можете вручную ввести имя атрибута в свою программу с консоли.

Опять же, если атрибут не найден, вы можете установить какое-то значение по умолчанию, которое позволяет нам заполнить некоторые из наших неполных данных.

Также, если ваш класс Student находится в стадии разработки, мы можем использовать функцию getattr() для завершения другого кода. Как только класс получит этот атрибут, он автоматически подберет его и не будет использовать значение по умолчанию.

Источник

Разбираемся с доступом к атрибутам в Python

Ну и что с того? Зачем думать о том, как Python за меньший синтаксис делает больше вызовов функций? На самом деле для этого есть две причины. Во-первых, полезно знать, как на самом деле работает Python, чтобы лучше понимать/отлаживать код, когда что-то идет не так как надо. Во-вторых, так можно выявить минимум, необходимый для реализации языка.

Именно поэтому, чтобы заняться самообразованием и заодно подумать, что может понадобиться для реализации Python под WebAssembly или API bare bones на C, я решил написать эту статью о том, как выглядит доступ к атрибутам и что скрывается за синтаксисом.

Теперь вы можете попытаться собрать воедино все, что относится к доступу к атрибутам, прочитав справочник по Python. Так вы можете прийти к выражениям ссылок на атрибуты и модели данных для настройки доступа к атрибутам, однако, все равно важно связать все вместе, чтобы понять, как работает доступ. Поэтому я предпочитаю идти от исходного кода на CPython и выяснять, что происходит в интерпретаторе (я специально использую тег репозитория CPython 3.8.3, поскольку у меня есть стабильные ссылки и я использую последнюю версию на момент написания статьи).

В начале статьи вам встретится немного кода на С, но я не жду, что вы досконально поймете, что там происходит. Я напишу о том, что нужно будет из него понять, поэтому если у вас нет ни малейших знаний в С, то ничего страшного, вы все равно поймете все то, о чем я говорю.

Читайте также:  радио маруся ступино какая волна

Смотрим в байткод

Итак, давайте разберемся со следующим выражением:

Наверное, самое простая отправная точка в изучении – это байткод. Посмотрим на эту строку и разберемся, что делает компилятор:

Самый важный код операции здесь — LOADATTR. Если интересно, он заменяет объект на вершине стека результатом доступа к именованному атрибуту, как указано в conames[i] .

Большая часть этого кода – это просто работа со стеком, его мы можем опустить. Ключевой бит – это вызов PyObject_GetAttr(), который и обеспечивает доступ к атрибутам.

Имя этой функции выглядит знакомо

Теперь это имя выглядит прямо как getattr(), только в соглашении об именовании функций в С, которое используется в CPython. Покопавшись в Python/bltinmodule.c, где лежат все встроенные модули Python, можем проверить, верна ли наша догадка. Поискав по «getattr» в файле, вы найдете строку, которая связывает имя «getattr» с функцией «builtin_getattr()»

Разбираемся с getattr()

Что мы уже знаем

Запись функции для getattr()

Поиск атрибутов с помощью специальных методов

Обработка типа объекта осуществляется специально, поскольку это позволяет ускорить поиск и доступ. В целом, это исключает дополнительный поиск, пропуская экземпляр каждый раз, когда мы что-то ищем. На уровне CPython это позволяет заводить специальные методы, которые находятся в поле struct для ускорения поиска. Поэтому несмотря на то, что кажется немного странным игнорировать объект, а вместо него использовать тип, это имеет определенный смысл.

Теперь во имя простоты я немного схитрю и заставлю getattr() обрабатывать методы getattribute() и getattr() явно, в то время как CPython производит некоторые манипуляции под капотом, чтобы заставить объект обрабатывать оба метода самостоятельно. В конечном счете, семантика наших целей получается одинаковой.

Псевдокод, реализующий getattr()

Разбираемся с object.getattribute()

В поисках дескриптора данных

Первая важная вещь, которую мы собираемся сделать в object.getattribute() – это поиск дескриптора данных для типа. Если вы никогда не слышали о дескрипторах, то расскажу – это способ программно управлять тем, как работает отдельный атрибут. Возможно, вы вообще никогда о них не слышали, но, если вы некоторое время уже используете Python, я подозреваю, что вы уже использовали дескрипторы: свойства, classmethod и staticmethod – все это дескрипторы.

Если же у самого объекта нет атрибута, то мы увидим, есть ли там дескриптор без данных. Поскольку мы уже искали дескриптор ранее, то можем предположить, что если он был найден, но еще не использовался, когда мы искали дескриптор данных, то это дескриптор без данных.

Наконец, мы нашли атрибут типа, и он не был дескриптором, теперь мы возвращаем его. В итоге, порядок поиска атрибутов выглядит следующим образом:

Дескриптор данных ищется по типам;

Дескриптор без данных ищется по типам;

Что угодно ищется по типам.

Вы заметите, что сначала мы ищем какой-то дескриптор, затем, если нам это не удалось, мы ищем обычный объект, который соответствует виду дескриптора, который мы искали. Сначала мы ищем данные, потом уже что-то другое. Все это имеет смысл, если думать о том как метод self.attr = val в init() хранит данные об объекте. Скорее всего, если вы столкнулись с этим, то хотите, чтобы это стояло перед методом или чем-то подобным. И вам в первую очередь нужны дескрипторы, поскольку, если вы программно определили атрибут, то вероятно, хотели бы, чтобы он использовался всегда.

Заключение

Как видите, во время поиска атрибутов в Python происходит много интересного. Несмотря на то, что я бы сказал, что ни одна из частей не является концептуально сложной, в сумме мы получаем множество операций. Именно поэтому некоторые программисты пытаются минимизировать доступ к атрибутам в Python, чтобы избегать всего этого механизма, если речь идет о важности производительности.

Так исторически сложилось, что почти вся эта семантика пришла в Python как часть классов нового стиля, а не «классических». Это различие исчезло в Python 3, когда классические классы остались в прошлом, так что если вы ничего о них не слышали, то это и хорошо, наверное.

Другие статьи из этой серии можно найти по тегу «syntactic sugar» в этом блоге. Код из этой статьи вы найдете здесь.

Источник

Сказочный портал