Я пытаюсь понять, когда использовать __getattr__
или __getattribute__
. Документация упоминает __getattribute__
относится к классам нового стиля. Что такое классы нового стиля?
python
getattr
getattribute
Ярина
источник
источник
Ответы:
Ключевое различие между
__getattr__
и__getattribute__
заключается в том, что__getattr__
он вызывается только в том случае, если атрибут не был найден обычными способами. Это хорошо для реализации запасного варианта для отсутствующих атрибутов и, вероятно, является одним из двух, которые вы хотите.__getattribute__
вызывается перед просмотром фактических атрибутов объекта, и поэтому может быть сложно реализовать правильно. Вы можете очень легко оказаться в бесконечных рекурсиях.Классы нового стиля являются производными, классы
object
старого стиля - это классы в Python 2.x без явного базового класса. Но различие между классами старого и нового стиля не является важным при выборе между__getattr__
и__getattribute__
.Вы почти наверняка хотите
__getattr__
.источник
__getattribute__
будет вызываться для каждого доступа, и__getattr__
будет вызываться для времен, которые__getattribute__
поднялиAttributeError
. Почему бы просто не сохранить все это в одном?__getattribute__
.objec.__getattribute__
вызываетmyclass.__getattr__
при правильных обстоятельствах.Давайте посмотрим на несколько простых примеров обоих
__getattr__
и__getattribute__
магических методов.__getattr__
Python будет вызывать
__getattr__
метод всякий раз, когда вы запрашиваете атрибут, который еще не был определен. В следующем примере мой класс Count не имеет__getattr__
метода. Теперь в основном, когда я пытаюсь получить доступ к обоимobj1.mymin
иobj1.mymax
атрибутам все работает нормально. Но когда я пытаюсь получить доступ кobj1.mycurrent
атрибуту - Python дает мнеAttributeError: 'Count' object has no attribute 'mycurrent'
Теперь у моего класса Count есть
__getattr__
метод. Теперь, когда я пытаюсь получить доступ кobj1.mycurrent
атрибуту - Python возвращает мне все, что я реализовал в моем__getattr__
методе. В моем примере всякий раз, когда я пытаюсь вызвать атрибут, который не существует, python создает этот атрибут и устанавливает для него целочисленное значение 0.__getattribute__
Теперь давайте посмотрим на
__getattribute__
метод. Если у вас есть__getattribute__
метод в вашем классе, python вызывает этот метод для каждого атрибута независимо от того, существует он или нет. Так зачем нам__getattribute__
метод? Хорошая причина в том, что вы можете запретить доступ к атрибутам и сделать их более безопасными, как показано в следующем примере.Всякий раз, когда кто-то пытается получить доступ к моим атрибутам, начинается с подстроки 'cur' python вызывает
AttributeError
исключение. В противном случае он возвращает этот атрибут.Важно: чтобы избежать бесконечной рекурсии в
__getattribute__
методе, его реализация всегда должна вызывать метод базового класса с тем же именем для доступа к любым необходимым ему атрибутам. Например:object.__getattribute__(self, name)
илиsuper().__getattribute__(item)
нетself.__dict__[item]
ВАЖНЫЙ
Если ваш класс содержит магические методы getattr и getattribute, то он
__getattribute__
вызывается первым. Но если__getattribute__
возникаетAttributeError
исключение, то исключение будет проигнорировано, и__getattr__
метод будет вызван. Смотрите следующий пример:источник
__getattribute__
но, конечно, это не так. Потому что, согласно вашему примеру, все, что вы делаете, это__getattribute__
вызываетAttributeError
исключение, если атрибут отсутствует в__dict__
объекте; но вам это на самом деле не нужно, потому что это стандартная реализация,__getattribute__
а infact__getattr__
- именно то, что вам нужно в качестве резервного механизма.current
определяется по экземплярамCount
(см__init__
), так что просто повышение ,AttributeError
если атрибут не существует , не совсем то , что происходит - это сдвинуто__getattr__
для всех имен , начиная «пса», в том числеcurrent
, ноcurious
,curly
...Это всего лишь пример, основанный на объяснении Неда Батчелдера .
__getattr__
пример:И если использовать тот же пример с
__getattribute__
You, вы получите >>>RuntimeError: maximum recursion depth exceeded while calling a Python object
источник
__getattr__()
Реализации реального мира принимают только конечный набор допустимых имен атрибутов, вызываяAttributeError
недопустимые имена атрибутов, тем самым избегая тонких и трудных для отладки проблем . Этот пример безоговорочно принимает все имена атрибутов как допустимые - странное (и откровенно подверженное ошибкам) неправильное использование__getattr__()
. Если вы хотите "полный контроль" над созданием атрибутов, как в этом примере, вы хотите__getattribute__()
вместо этого.defaultdict
.__getattr__
он будет вызван перед поиском суперкласса. Это нормально для прямого подклассаobject
, поскольку единственные методы, которые вас действительно волнуют, это магические методы, которые в любом случае игнорируют экземпляр, но для любой более сложной структуры наследования вы полностью исключаете возможность наследовать что-либо от родительского объекта.Классы нового стиля наследуются
object
от другого класса нового стиля:Классы старого стиля не делают:
Это относится только к Python 2 - в Python 3 все вышеперечисленное создаст классы нового стиля.
См. 9. Классы (руководство по Python), NewClassVsClassicClass и В чем разница между классами старого и нового стилей в Python? для деталей.
источник
Классы нового стиля подклассы «объект» (прямо или косвенно). У них есть
__new__
метод класса в дополнение к__init__
более рациональному низкоуровневому поведению.Обычно вы хотите переопределить
__getattr__
(если вы переопределите и то и другое), иначе вам будет сложно поддерживать синтаксис «self.foo» в ваших методах.Дополнительная информация: http://www.devx.com/opensource/Article/31482/0/page/4
источник
Читая через Beazley & Jones PCB, я наткнулся на явный и практический пример использования
__getattr__
который помогает ответить на вопрос «когда» в вопросе OP. Из книги:«Этот
__getattr__()
метод является своего рода универсальным средством поиска атрибутов. Это метод, который вызывается, если код пытается получить доступ к атрибуту, который не существует». Мы знаем это из приведенных выше ответов, но в рецепте PCB 8.15 эта функциональность используется для реализации шаблона проектирования делегирования . Если у Объекта A есть атрибут Object B, который реализует много методов, которым Object A хочет делегировать, вместо того, чтобы переопределять все методы Object B в Object A просто для вызова методов Object B, определите__getattr__()
метод следующим образом:где _b - это имя атрибута объекта A, который является объектом B. Когда метод, определенный в объекте B, вызывается для объекта A,
__getattr__
метод будет вызван в конце цепочки поиска. Это также сделает код чище, поскольку у вас нет списка методов, определенных только для делегирования другому объекту.источник