__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 aatrybutu f. To wzywa f.__getattribute__('a'). __getattribute__następnie próbuje załadować self.__dict__. __dict__jest atrybutem, self == fdlatego 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ż
fma aatrybut.
- 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, selfa 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.