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