
Python — динамически типизированный язык программирования, что означает, что мы можем изменять атрибуты и методы объектов в реальном времени. Однако для контроля за доступом к атрибутам и их модификацией, Python предлагает некоторые встроенные методы, такие как __getattr__() и __getattribute__().
Метод __getattr__(self, name) вызывается, когда попытка чтения атрибута name не удается, т.е. когда атрибута нет в экземпляре и классе. В противном случае, этот метод не вызывается. Этот метод возвращает значение для несуществующего атрибута, если он был определен.
class Test:
def __init__(self, a, b):
self.a = a
self.b = b
def __getattr__(self, attr):
return f"{attr} не существует"
obj = Test(5, 10)
print(obj.a) # Выведет: 5
print(obj.c) # Выведет: c не существуетМетод __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__() состоит в том, что __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: {name}")
obj = DynamicAttr({'a': 1, 'b': 2})
print(obj.a) # Выведет: 1
print(obj.b) # Выведет: 2Помимо __getattr__() и __getattribute__() существуют ещё и методы __setattr__() и __delattr__(), которые соответственно позволяют контролировать изменение и удаление атрибутов. Они также очень полезны и часто используются вместе с __getattr__() и __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. Они могут быть особенно полезны для реализации паттернов проектирования, таких как прокси, или при работе с объектами, у которых атрибуты меняются в реальном времени. Тем не менее, с этими методами следует обращаться осторожно, чтобы избежать нежелательных побочных эффектов, таких как зацикливание.
Содержание: