Магические методы Python и __getattr__
Магические методы - это общий термин, относящийся к «специальным» методам классов Python. Для них нет единого определения, поскольку их применение разнообразно. Пример нескольких распространенных и известных магических методов:
Магические методы - это общий термин, относящийся к «специальным» методам классов Python. Для них нет единого определения, поскольку их применение разнообразно. Пример нескольких распространенных и известных магических методов:
- init - выполняет функцию инициализатора объекта (иногда неправильно называемого конструктором)
- str - строковое представление объекта
- add- перегрузка оператора +.
Что общего у всех этих методов? Очевидно, что все они начинаются и заканчиваются двойными подчеркиваниями (). Но помимо этого, что делает их «магическими методами», это то, что они вызываются как-то «специфически». Эти методы не вызываются вручную, Python это делает за нас. Например, при выполнении str(obj), выполняется вызов obj. str __ ().
Есть множество магических методов, но в этой статье будет рассмотрены getattr и getattribute.
getattr
Этот метод позволит вам «улавливать» ссылки на атрибуты, которые не существуют в объекте. Рассмотрим простой пример, чтобы понять, как это работает:
class Dummy(object):
pass
d = Dummy()
d.does_not_exist # Сбой с AttributeError
В этом примере доступ к атрибуту завершается с ошибкой (вызов исключения AttributeError), поскольку атрибут does_not_exist не существует.
Используя магический метод __getattr__можно перехватить поиск несуществующего атрибута и выполнить некоторое действие:
class Dummy(object):
def __getattr__(self, attr):
return attr.upper()
d = Dummy()
d.does_not_exist
d.what_about_this_one
Но если атрибут существует, то getattr вызываться не будет:
class Dummy(object):
def __getattr__(self, attr):
return attr.upper()
d = Dummy()
d.value = "Python"
print(d.value) # "Python"
getattribute
getattribute похож на getattr, с важным отличием, что getattribute будет ВСЕГДА перехватывать поиск атрибута и не имеет значения, существует ли атрибут или нет. Рассмотрим простой пример:
class Dummy(object):
def __getattribute__(self, attr):
return '1111111111'
d = Dummy()
d.value = "Python"
print(d.value) # "1111111111"
В этом примере объект d уже содержит атрибут value. Но когда запрашивается доступ к нему, мы не получаем первоначальное ожидаемое значение («Python»), а вместо него получаем возвращаемое getattribute. Это означает, что был фактически потерян атрибут value, т. е. он стал «недостижимым».
Если вам когда-либо понадобится использовать getattribute для моделирования чего-то похожего на getattr, нужно сделать ещё более сложную обработку Python:
class Dummy(object):
def __getattribute__(self, attr):
__dict__ = super(Dummy, self).__getattribute__('__dict__')
if attr in __dict__:
return super(Dummy, self).__getattribute__(attr)
return attr.upper()
d = Dummy()
d.value = "Python"
print(d.value) # "Python"
print(d.does_not_exist) # "DOES_NOT_EXIST"
Более реалистичный пример
getattr позволяет определить поведение экземпляра пользовательского типа при попытке получения значения атрибута. Хорошим примером может быть расширение базового кортежа Python, чтобы добавить к нему немого синтаксического сахара:
a_tuple = ("z", 3, "Python", -1)
К элементам обращаемся по-другому:
print(a_tuple._1) // "z"
print(a_tuple._3) // "Python"
Каждый элемент в кортеже получает доступ как атрибут, причем первым элементом является атрибут _1, второй _2 и т. д.
Можно легко расширить общий кортеж Python, чтобы соответствовать этому поведению, для этого напишем очень простой код:
class Tuple(tuple):
def __getattr__(self, name):
def _int(val):
try:
return int(val)
except ValueError:
return False
if not name.startswith('_') or not _int(name[1:]):
raise AttributeError("'tuple' object has no attribute '%s'" % name)
index = _int(name[1:]) - 1
return self[index]
t = Tuple(['z', 3, 'Python', -1])
print(t._1) # 'z'
print(t._2) # 3
print(t._3) # 'Python'
Результаты выполнения скрипта:
t = Tuple(['z', 3, 'Python', -1])
assert t._1 == 'z'
assert t._2 == 3
assert t._3 == 'Python'
assert t._4 == -1
В этом примере можно увидеть, как улавливаются отсутствующие атрибуты с помощью getattr, но если этот атрибут не найден в формате _n (где n - целое число), производится вызов исключения AttributeError вручную.
Вывод
Магические методы - отличный механизм расширения базовых функций классов и объектов Python и обеспечения более интуитивно понятных интерфейсов. getattr предоставляет возможности динамической генерации отсутствующих атрибутов. Но будьте осторожны с getattribute , т. к для правильной реализации понадобится хитрость, при этом не теряя Python атрибуты в пустую.