Python getattr self module

Python getattr() Function Explained

Python getattr() function

In this article we will explore how to use the Python getattr() function.

Table of Contents

Introduction

Python getattr() function is a built-in function that allows to dynamically access attributes of an object. Specifically, it is used to retrieve names attributes of Python objects.

The syntax of the Python getattr() function is:

getattr(object, name[, default])
  • object – Python object from which we want to retrieve an attribute
  • name – name of the named attribute of a Python object

The getattr() function, when called, searches for a specified named attribute of a given Python object and returns its value.

In the following sections we will explore some of the common use cases for getattr() function.

Accessing attributes of objects dynamically using getattr()

One of the most popular use cases for the Python getattr() function is accessing attributes of an object dynamically.

Let’s start by creating a new Python object Car that has three attributes (make, model, price):

Next, we will create an instance of this class with some sample values:

Now we can use getattr() function to access attributes of this class dynamically.

For example, let’s say we want to retrieve the price attribute for the car object we just created:

In case you try to retrieve the attribute that the object doesn’t have, you will see an AttributeError.

For example, this object has no attribute colour, so let’s see what happens when we try to retrieve it:

AttributeError: 'Car' object has no attribute 'colour'

This approach is very useful if you are working with multiple classes where you don’t know whether they necessarily have the attributes you are looking for, and it can save a lot of time and code amount to quickly run these tests to retrieve attributes’ values.

Building Dynamic APIs using getattr()

Another use cases for the Python getattr() function is building dynamic APIs in Python.

Let’s start by creating a simple Calculator class with a few methods that perform mathematical calculations:

Now we can build an API around this Calculator class, that will allow to call any of the methods dynamically (using Python getattr() function):

Once the API is built, we can test it with different calculations like addition and subtraction and check the results:

In this example we use the Python getattr() function to dynamically access the required method of a Python class.

Loading modules dynamically using getattr()

Another use cases for the Python getattr() function is loading modules dynamically at runtime in Python.

In this example we will use a built-in Python module importlib , which is an implementation of the import statement. Specifically, we will work with import_module() function for programmatic importing.

We will use the getattr() function to access specific functions in the loaded module.

Let’s say we want to build a small program that asks the user which module to import, which function from that module to access, and what operation to perform.

For example, we want to import the math module, access the sqrt() function and find the square root of 25.

We are going to load the module and function programmatically and perform the calculation:

While this is a very simplistic example that doesn’t look like a useful application of the sqrt() function, it illustrates the general idea of loading modules and functions dynamically.

Conclusion

In this article we explored the Python getattr() function.

Now that you know the basic functionality, you can practice using it in your projects to add more functionality to the code.

Источник

Атрибуты и протокол дескриптора в Python

Вы, возможно, уже знаете, что у большинства объектов есть внутренний словарь __dict__, содержащий все их аттрибуты. И что особенно радует, как легко можно изучать такие низкоуровневые детали в Питоне:

Давайте начнём с попытки сформулировать такую (неполную) гипотезу:

foo.bar эквивалентно foo.__dict__[‘bar’] .

Пока звучит похоже на правду:

>>> foo = Foo() >>> foo.__dict__['bar'] 'hello!'

Теперь предположим, что вы уже в курсе, что в классах можно объявлять динамические аттрибуты:

>>> class Foo: . def __init__(self): . self.bar = 'hello!' . . def __getattr__(self, item): . return 'goodbye!' . . foo = Foo() >>> foo.bar 'hello!' >>> foo.baz 'goodbye!' >>> foo.__dict__

Хм… ну ладно. Видно что __getattr__ может эмулировать доступ к «ненастоящим» атрибутам, но не будет работать, если уже есть объявленная переменная (такая, как foo.bar, возвращающая ‘hello!’, а не ‘goodbye!’). Похоже, всё немного сложнее, чем казалось вначале.

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

Пока что модифицируем нашу гипотезу так:

foo.bar эквивалентно foo.__getattribute__(‘bar’), что примерно работает так:

def __getattribute__(self, item): if item in self.__dict__: return self.__dict__[item] return self.__getattr__(item)

Проверим практикой, реализовав этот метод (под другим именем) и вызывая его напрямую:

>>> class Foo: . def __init__(self): . self.bar = 'hello!' . . def __getattr__(self, item): . return 'goodbye!' . . def my_getattribute(self, item): . if item in self.__dict__: . return self.__dict__[item] . return self.__getattr__(item) >>> foo = Foo() >>> foo.bar 'hello!' >>> foo.baz 'goodbye!' >>> foo.my_getattribute('bar') 'hello!' >>> foo.my_getattribute('baz') 'goodbye!'

Отлично, осталось лишь проверить, что поддерживается присвоение переменных, после чего можно расходиться по дом… —

>>> foo.baz = 1337 >>> foo.baz 1337 >>> foo.my_getattribute('baz') = 'h4x0r' SyntaxError: can't assign to function call

my_getattribute возвращает некий объект. Мы можем изменить его, если он мутабелен, но мы не можем заменить его на другой с помощью оператора присвоения. Что же делать? Ведь если foo.baz это эквивалент вызова функции, как мы можем присвоить новое значение атрибуту в принципе?

Когда мы смотрим на выражение типа foo.bar = 1, происходит что-то больше, чем просто вызов функции для получения значения foo.bar. Похоже, что присвоение значения атрибуту фундаментально отличается от получения значения атрибута. И правда: мы может реализовать __setattr__, чтобы убедиться в этом:

>>> class Foo: . def __init__(self): . self.__dict__['my_dunder_dict'] = <> . self.bar = 'hello!' . . def __setattr__(self, item, value): . self.my_dunder_dict[item] = value . . def __getattr__(self, item): . return self.my_dunder_dict[item] >>> foo = Foo() >>> foo.bar 'hello!' >>> foo.bar = 'goodbye!' >>> foo.bar 'goodbye!' >>> foo.baz Traceback (most recent call last): File "", line 1, in foo.baz File "", line 10, in __getattr__ return self.my_dunder_dict[item] KeyError: 'baz' >>> foo.baz = 1337 >>> foo.baz 1337 >>> foo.__dict__ >

Пара вещей на заметку относительно этого кода:

  1. __setattr__ не имеет своего аналога __getattribute__ (т.е. магического метода __setattribute__ не существует).
  2. __setattr__ вызывается внутри __init__, именно поэтому мы вынуждены делать self.__dict__[‘my_dunder_dict’] = <> вместо self.my_dunder_dict = <>. В противном случае мы столкнулись бы с бесконечной рекурсией.

А ведь у нас есть ещё и property (и его друзья). Декоратор, который позволяет методам выступать в роли атрибутов.

Давайте постараемся понять, как это происходит.

>>> class Foo(object): . def __getattribute__(self, item): . print('__getattribute__ was called') . return super().__getattribute__(item) . . def __getattr__(self, item): . print('__getattr__ was called') . return super().__getattr__(item) . . @property . def bar(self): . print('bar property was called') . return 100 >>> f = Foo() >>> f.bar __getattribute__ was called bar property was called

Просто ради интереса, а что у нас в f.__dict__?

>>> f.__dict__ __getattribute__ was called <>

В __dict__ нет ключа bar, но __getattr__ почему-то не вызывается. WAT?

bar — метод, да ещё и принимающий в качестве параметра self, вот только это метод находится в классе, а не в экземпляре класса. И в этом легко убедиться:

>>> Foo.__dict__ mappingproxy(, '__doc__': None, '__getattr__': , '__getattribute__': , '__module__': '__main__', '__weakref__': , 'bar': >)

Ключ bar действительно находится в словаре атрибутов класса. Чтобы понять работу __getattribute__, нам нужно ответить на вопрос: чей __getattribute__ вызывается раньше — класса или экземпляра?

>>> f.__dict__['bar'] = 'will we see this printed?' __getattribute__ was called >>> f.bar __getattribute__ was called bar property was called 100

Видно, что первым делом проверка идёт в __dict__ класса, т.е. у него приоритет перед экземпляром.

Погодите-ка, а когда мы вызывали метод bar? Я имею в виду, что наш псевдокод для __getattribute__ никогда не вызывает объект. Что же происходит?

descr.__get__(self, obj, type=None) -> value

descr.__set__(self, obj, value) -> None

descr.__delete__(self, obj) -> None

Вся суть тут. Реализуйте любой из этих трёх методов, чтобы объект стал дескриптором и мог менять дефолтное поведение, когда с ним работают как с атрибутом.

Если объект объявляет и __get__(), и __set__(), то его называют дескриптором данных («data descriptors»). Дескрипторы реализующие лишь __get__() называются дескрипторами без данных («non-data descriptors»).

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

Чтобы создать дескриптор данных доступный только для чтения, объявите и __get__(), и __set__(), где __set__() кидает AttributeError при вызове. Реализации такого __set__() достаточно для создания дескриптора данных.

Короче говоря, если вы объявили любой из этих методов — __get__, __set__ или __delete__, вы реализовали поддержку протокола дескриптора. А это именно то, чем занимается декоратор property: он объявляет доступный только для чтения дескриптор, который будет вызываться в __getattribute__.

Последнее изменение нашей реализации:

foo.bar эквивалентно foo.__getattribute__(‘bar’), что примерно работает так:

def __getattribute__(self, item): if item in self.__class__.__dict__: v = self.__class__.__dict__[item] elif item in self.__dict__: v = self.__dict__[item] else: v = self.__getattr__(item) if hasattr(v, '__get__'): v = v.__get__(self, type(self)) return v

Попробуем продемонстрировать на практике:

class Foo: class_attr = "I'm a class attribute!" def __init__(self): self.dict_attr = "I'm in a dict!" @property def property_attr(self): return "I'm a read-only property!" def __getattr__(self, item): return "I'm dynamically returned!" def my_getattribute(self, item): if item in self.__class__.__dict__: print('Retrieving from self.__class__.__dict__') v = self.__class__.__dict__[item] elif item in self.__dict__: print('Retrieving from self.__dict__') v = self.__dict__[item] else: print('Retrieving from self.__getattr__') v = self.__getattr__(item) if hasattr(v, '__get__'): print("Invoking descriptor's __get__") v = v.__get__(self, type(self)) return v
>>> foo = Foo() . . print(foo.class_attr) . print(foo.dict_attr) . print(foo.property_attr) . print(foo.dynamic_attr) . . print() . . print(foo.my_getattribute('class_attr')) . print(foo.my_getattribute('dict_attr')) . print(foo.my_getattribute('property_attr')) . print(foo.my_getattribute('dynamic_attr')) I'm a class attribute! I'm in a dict! I'm a read-only property! I'm dynamically returned! Retrieving from self.__class__.__dict__ I'm a class attribute! Retrieving from self.__dict__ I'm in a dict! Retrieving from self.__class__.__dict__ Invoking descriptor's __get__ I'm a read-only property! Retrieving from self.__getattr__ I'm dynamically returned!

Мы лишь немного поскребли поверхность реализации атрибутов в Python. Хотя наша последняя попытка эмулировать foo.bar в целом корректна, учтите, что всегда могут найтись небольшие детали, реализованные по-другому.

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

Источник

Читайте также:  Assertion and exception in java
Оцените статью