__getattribute__
jest wywoływany za każdym razem, gdy nastąpi dostęp do atrybutu.
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
Spowoduje to nieskończoną rekurencję. Sprawcą jest tutaj linia return self.__dict__[attr]
. Udawajmy (jest wystarczająco blisko prawdy), że wszystkie atrybuty są przechowywane self.__dict__
i dostępne według ich nazw. Linia
f.a
próbuje uzyskać dostęp do a
atrybutu f
. To wzywa f.__getattribute__('a')
. __getattribute__
następnie próbuje załadować self.__dict__
. __dict__
jest atrybutem, self == f
dlatego wywołania w języku Python, f.__getattribute__('__dict__')
które ponownie próbują uzyskać dostęp do atrybutu '__dict__
”. To jest nieskończona rekurencja.
Jeśli __getattr__
zamiast tego został użyty
- Nigdy by się nie uruchomił, ponieważ
f
ma a
atrybut.
- Gdyby zadziałał (powiedzmy, że prosiłeś
f.b
), nie zostałby wywołany w celu znalezienia, __dict__
ponieważ już tam __getattr__
jest i jest wywoływany tylko wtedy, gdy wszystkie inne metody znalezienia atrybutu zawiodły .
„Prawidłowym” sposobem na napisanie powyższej klasy __getattribute__
jest
class Foo(object):
# Same __init__
def __getattribute__(self, attr):
return super(Foo, self).__getattribute__(attr)
super(Foo, self).__getattribute__(attr)
wiąże __getattribute__
metodę „najbliższej” nadklasy (formalnie następnej klasy w klasie klasy Resolution Resolution Order lub MRO) z bieżącym obiektem, self
a następnie wywołuje ją i pozwala na wykonanie tej pracy.
Wszystkich tych problemów można uniknąć, używając __getattr__
Pythona, który robi to normalnie, dopóki nie zostanie znaleziony atrybut. W tym momencie Python przekazuje kontrolę nad twoją __getattr__
metodą i pozwala jej coś wymyślić.
Warto również zauważyć, że możesz spotkać się z nieskończoną rekurencją __getattr__
.
class Foo(object):
def __getattr__(self, attr):
return self.attr
Zostawię to jako ćwiczenie.