Getattr vs getattribute in python

Understanding the difference between __getattr__ and __getattribute__

I am trying to understand the difference between __getattr__ and __getattribute__ , however, I am failing at it. The answer to the Stack Overflow question Difference between __getattr__ vs __getattribute__ says:

__getattribute__ is invoked before looking at the actual attributes on the object, and so can be tricky to implement correctly. You can end up in infinite recursions very easily.

Why? I read that if __getattribute__ fails, __getattr__ is called. So why are there two different methods doing the same thing? If my code implements the new style classes, what should I use? I am looking for some code examples to clear this question. I have Googled to best of my ability, but the answers that I found don’t discuss the problem thoroughly. If there is any documentation, I am ready to read that.

if it helps, from the docs «In order to avoid infinite recursion in this method, its implementation should always call the base class method with the same name to access any attributes it needs, for example, object.__getattribute__(self, name).» [docs.python.org/2/reference/…

4 Answers 4

Some basics first.

With objects, you need to deal with their attributes. Ordinarily, we do instance.attribute . Sometimes we need more control (when we do not know the name of the attribute in advance).

Читайте также:  Php file stream wrapper

For example, instance.attribute would become getattr(instance, attribute_name) . Using this model, we can get the attribute by supplying the attribute_name as a string.

Use of __getattr__

You can also tell a class how to deal with attributes which it doesn’t explicitly manage and do that via __getattr__ method.

Python will call this method whenever you request an attribute that hasn’t already been defined, so you can define what to do with it.

class A(dict): def __getattr__(self, name): return self[name] a = A() # Now a.somekey will give a['somekey'] 

Caveats and use of __getattribute__

If you need to catch every attribute regardless whether it exists or not, use __getattribute__ instead. The difference is that __getattr__ only gets called for attributes that don’t actually exist. If you set an attribute directly, referencing that attribute will retrieve it without calling __getattr__ .

__getattribute__ is called all the times.

» For example, instance.attribute would become getattr(instance, attribute_name) . » . Shouldn’t it be __getattribute__(instance, attribute_name) ?

__getattribute__ is called whenever an attribute access occurs.

class Foo(object): def __init__(self, a): self.a = 1 def __getattribute__(self, attr): try: return self.__dict__[attr] except KeyError: return 'default' f = Foo(1) f.a 

This will cause infinite recursion. The culprit here is the line return self.__dict__[attr] . Let’s pretend (It’s close enough to the truth) that all attributes are stored in self.__dict__ and available by their name. The line

attempts to access the a attribute of f . This calls f.__getattribute__(‘a’) . __getattribute__ then tries to load self.__dict__ . __dict__ is an attribute of self == f and so python calls f.__getattribute__(‘__dict__’) which again tries to access the attribute ‘__dict__ ‘. This is infinite recursion.

If __getattr__ had been used instead then

  1. It never would have run because f has an a attribute.
  2. If it had run, (let’s say that you asked for f.b ) then it would not have been called to find __dict__ because it’s already there and __getattr__ is invoked only if all other methods of finding the attribute have failed.

The ‘correct’ way to write the above class using __getattribute__ is

class Foo(object): # Same __init__ def __getattribute__(self, attr): return super().__getattribute__(attr) 

super().__getattribute__(attr) binds the __getattribute__ method of the ‘nearest’ superclass (formally, the next class in the class’s Method Resolution Order, or MRO) to the current object self and then calls it and lets that do the work.

All of this trouble is avoided by using __getattr__ which lets Python do it’s normal thing until an attribute isn’t found. At that point, Python hands control over to your __getattr__ method and lets it come up with something.

It’s also worth noting that you can run into infinite recursion with __getattr__ .

class Foo(object): def __getattr__(self, attr): return self.attr 

I’ll leave that one as an exercise.

Источник

Магические методы __getattr__() и __getattribute__() в Python

Магические методы __getattr__() и __getattribute__() в Python

Python — динамически типизированный язык программирования, что означает, что мы можем изменять атрибуты и методы объектов в реальном времени. Однако для контроля за доступом к атрибутам и их модификацией, Python предлагает некоторые встроенные методы, такие как __getattr__() и __getattribute__() .

Синтаксис и использование __getattr__()

Метод __getattr__(self, name) вызывается, когда попытка чтения атрибута name не удается, т.е. когда атрибута нет в экземпляре и классе. В противном случае, этот метод не вызывается. Этот метод возвращает значение для несуществующего атрибута, если он был определен.

class Test: def __init__(self, a, b): self.a = a self.b = b def __getattr__(self, attr): return f" не существует" obj = Test(5, 10) print(obj.a) # Выведет: 5 print(obj.c) # Выведет: c не существует

Синтаксис и использование __getattribute__()

Метод __getattribute__(self, name) вызывается при попытке получить любой атрибут объекта. Он перехватывает любые попытки обращения к атрибутам, независимо от того, существуют они или нет.

class Test: def __init__(self, a, b): self.a = a self.b = b def __getattribute__(self, attr): if attr == "a": return 0 else: return object.__getattribute__(self, attr) obj = Test(5, 10) print(obj.a) # Выведет: 0 print(obj.b) # Выведет: 10

Обратите внимание, что object.__getattribute__(self, attr) используется для предотвращения зацикливания.

Отличия между __getattr__() и __getattribute__()

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

Следовательно, __getattribute__() может быть использован для перехвата всех попыток доступа к атрибутам, включая те, которые на самом деле существуют. Это может быть полезно в некоторых случаях, например, когда вам нужно вести лог обращений к атрибутам. Однако __getattribute__() следует использовать с осторожностью, поскольку он может легко привести к зацикливанию.

С другой стороны, __getattr__() может быть использован для обработки обращений к несуществующим атрибутам, что может быть полезно для создания объектов-прокси или объектов с динамическими атрибутами.

Применение в практической работе

Благодаря этим методам можно реализовывать интересные и сложные структуры в Python. Для примера рассмотрим класс, который динамически генерирует свойства из словаря:

class DynamicAttr: def __init__(self, attr_map): self._attr_map = attr_map def __getattr__(self, name): if name in self._attr_map: return self._attr_map[name] else: raise AttributeError(f"No such attribute: ") obj = DynamicAttr() print(obj.a) # Выведет: 1 print(obj.b) # Выведет: 2

Работа с __setattr__() и __delattr__()

Помимо __getattr__() и __getattribute__() существуют ещё и методы __setattr__() и __delattr__() , которые соответственно позволяют контролировать изменение и удаление атрибутов. Они также очень полезны и часто используются вместе с __getattr__() и __getattribute__() .

Осторожность при работе с __getattribute__()

Как было упомянуто ранее, __getattribute__() перехватывает любые попытки обращения к атрибутам, и это может легко привести к бесконечным рекурсивным вызовам. Например, предположим, что вы пытаетесь получить доступ к атрибуту внутри метода __getattribute__() :

class RecursiveAttr: def __init__(self, a): self.a = a def __getattribute__(self, name): return self.a # Это вызовет бесконечную рекурсию!

Python попытается вызвать __getattribute__() для обращения к атрибуту a, что приведет к еще одному вызову __getattribute__() и так далее. Вот почему в примере с __getattribute__() было использовано object.__getattribute__(self, attr) , чтобы обеспечить нормальное поведение.

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

Заключение

Методы __getattr__() и __getattribute__() дают вам возможность контролировать доступ к атрибутам ваших объектов в Python. Они могут быть особенно полезны для реализации паттернов проектирования, таких как прокси, или при работе с объектами, у которых атрибуты меняются в реальном времени. Тем не менее, с этими методами следует обращаться осторожно, чтобы избежать нежелательных побочных эффектов, таких как зацикливание.

Источник

Разница между __getattr__ и __getattribute__ в Python

Когда создаётся новый класс в Python, возникает потребность в определении поведения при обращении к атрибутам этого класса. Для этого в Python предусмотрены специальные методы: getattr и getattribute. Важно понимать разницу между ними и знать, когда и какой из них следует использовать.

Пример обращения к атрибуту класса

class MyClass: def __init__(self): self.my_attr = 10 obj = MyClass() print(obj.my_attr) # Выведет: 10

В данном коде создаётся экземпляр класса MyClass, у которого есть атрибут my_attr . При обращении к этому атрибуту, Python просто возвращает его значение.

Метод getattr

Если попытаться обратиться к атрибуту, которого нет в классе, Python вызовет специальный метод getattr. Это позволяет определить поведение класса при обращении к несуществующему атрибуту.

class MyClass: def __init__(self): self.my_attr = 10 def __getattr__(self, name): return f"Атрибут не существует" obj = MyClass() print(obj.my_attr) # Выведет: 10 print(obj.non_existent_attr) # Выведет: Атрибут non_existent_attr не существует

Метод getattribute

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

class MyClass: def __init__(self): self.my_attr = 10 def __getattribute__(self, name): return f"Пытаются получить атрибут " obj = MyClass() print(obj.my_attr) # Выведет: Пытаются получить атрибут my_attr print(obj.non_existent_attr) # Выведет: Пытаются получить атрибут non_existent_attr

Новые и старые стили классов

Новые стили классов в Python — это классы, которые являются прямыми или косвенными потомками object . В примерах выше использовались новые стили классов.

Старые стили классов — это классы, которые не наследуются от object . Они были доступны в Python 2, но в Python 3 все классы являются новыми стилями классов.

Заключение

Важно понимать разницу между методами getattr и getattribute. Первый вызывается только при обращении к несуществующему атрибуту, а второй — при обращении к любому атрибуту. Это позволяет определить поведение класса при работе с атрибутами в зависимости от потребностей.

Источник

Оцените статью