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