__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
Я оставлю это как упражнение.