__getattribute__ викликається кожен раз, коли відбувається доступ до атрибутів.
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
Це спричинить нескінченну рекурсію. Винуватець тут - лінія return self.__dict__[attr]. Давайте зробимо вигляд, що (це досить близько до істини), що всі атрибути зберігаються self.__dict__і доступні за їх іменем. Лінія
f.a
спроби отримати доступ до aатрибуту f. Це дзвінки f.__getattribute__('a'). __getattribute__потім намагається завантажити self.__dict__. __dict__є атрибутом self == fі так називає python, f.__getattribute__('__dict__')який знову намагається отримати доступ до атрибута '__dict__'. Це нескінченна рекурсія.
Якщо __getattr__замість цього використовувались
- Він ніколи не біг, бо
fмає aатрибут.
- Якби він запустився (скажімо, що ви просили
f.b), його не закликали б знайти, __dict__тому що він уже є, і __getattr__його викликають лише у тому випадку, якщо всі інші методи пошуку атрибута не вдалися .
«Правильний» спосіб писати вище клас , використовуючи __getattribute__це
class Foo(object):
# Same __init__
def __getattribute__(self, attr):
return super(Foo, self).__getattribute__(attr)
super(Foo, self).__getattribute__(attr)прив'язує __getattribute__метод "найближчого" суперкласу (формально, наступного класу в наказі методу роздільної здатності класу або MRO) до поточного об'єкта, selfа потім викликає його і дозволяє виконувати роботу.
Всю цю проблему можна уникнути, використовуючи __getattr__який дозволяє Python робити це нормальною справою, поки атрибут не знайдеться. У цей момент Python передає контроль над вашим __getattr__методом і дозволяє йому щось придумати.
Також варто зазначити, що ви можете зіткнутися з нескінченною рекурсією __getattr__.
class Foo(object):
def __getattr__(self, attr):
return self.attr
Я залишу це як вправу.