__getattribute__
wird aufgerufen, wenn ein Attributzugriff erfolgt.
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
Dies führt zu einer unendlichen Rekursion. Der Schuldige hier ist die Linie return self.__dict__[attr]
. Stellen wir uns vor (es ist nah genug an der Wahrheit), dass alle Attribute in self.__dict__
ihrem Namen gespeichert sind und unter diesem Namen verfügbar sind. Die Linie
f.a
versucht, auf das a
Attribut von zuzugreifen f
. Dies ruft auf f.__getattribute__('a')
. __getattribute__
versucht dann zu laden self.__dict__
. __dict__
ist ein Attribut von self == f
und so Python-Aufrufe, f.__getattribute__('__dict__')
die erneut versuchen, auf das Attribut zuzugreifen '__dict__
'. Dies ist eine unendliche Rekursion.
Wenn __getattr__
stattdessen verwendet worden wäre
- Es wäre nie gelaufen, weil
f
es ein a
Attribut hat.
- Wenn es ausgeführt worden wäre (sagen wir, dass Sie danach gefragt haben
f.b
), wäre es nicht zum Suchen aufgerufen worden, __dict__
da es bereits vorhanden ist und __getattr__
nur aufgerufen wird, wenn alle anderen Methoden zum Suchen des Attributs fehlgeschlagen sind .
Die 'richtige' Art, die obige Klasse mit zu schreiben, __getattribute__
ist
class Foo(object):
# Same __init__
def __getattribute__(self, attr):
return super(Foo, self).__getattribute__(attr)
super(Foo, self).__getattribute__(attr)
bindet die __getattribute__
Methode der 'nächsten' Oberklasse (formal die nächste Klasse in der Method Resolution Order oder MRO der Klasse) an das aktuelle Objekt self
und ruft sie dann auf und lässt dies die Arbeit erledigen.
All diese Probleme werden vermieden, indem __getattr__
Python so lange ausgeführt wird, bis ein Attribut nicht gefunden wird. An diesem Punkt übergibt Python die Kontrolle an Ihre __getattr__
Methode und lässt sie sich etwas einfallen lassen.
Es ist auch erwähnenswert, dass Sie mit in eine unendliche Rekursion geraten können __getattr__
.
class Foo(object):
def __getattr__(self, attr):
return self.attr
Ich werde das als Übung belassen.