__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
Я залишу це як вправу.