Ich versuche zu verstehen, wann __getattr__
oder __getattribute__
. Die Erwähnung der Dokumentation__getattribute__
gilt für Klassen neuen Stils. Was sind Klassen neuen Stils?
Ich versuche zu verstehen, wann __getattr__
oder __getattribute__
. Die Erwähnung der Dokumentation__getattribute__
gilt für Klassen neuen Stils. Was sind Klassen neuen Stils?
Antworten:
Ein wesentlicher Unterschied zwischen __getattr__
und __getattribute__
besteht darin, dass __getattr__
nur aufgerufen wird, wenn das Attribut nicht auf die übliche Weise gefunden wurde. Es ist gut für die Implementierung eines Fallbacks für fehlende Attribute und wahrscheinlich eines von zwei, die Sie möchten.
__getattribute__
wird aufgerufen, bevor die tatsächlichen Attribute des Objekts angezeigt werden, und kann daher schwierig zu implementieren sein. Sie können sehr leicht in unendliche Rekursionen geraten.
Klassen neuen Stils leiten sich von Klassen object
alten Stils ab, die in Python 2.x ohne explizite Basisklasse vorliegen. Bei der Wahl zwischen __getattr__
und ist jedoch die Unterscheidung zwischen Klassen im alten und im neuen Stil nicht wichtig __getattribute__
.
Sie wollen mit ziemlicher Sicherheit __getattr__
.
__getattribute__
wird für jeden Zugriff aufgerufen und __getattr__
wird für die Zeiten aufgerufen, __getattribute__
die eine ausgelöst haben AttributeError
. Warum nicht einfach alles in einem behalten?
__getattribute__
.
objec.__getattribute__
ruft myclass.__getattr__
unter den richtigen Umständen auf.
Sehen wir uns einige einfache Beispiele für beide __getattr__
und __getattribute__
magische Methoden an.
__getattr__
Python ruft die __getattr__
Methode auf, wenn Sie ein Attribut anfordern, das noch nicht definiert wurde. Im folgenden Beispiel hat meine Klasse Count keine __getattr__
Methode. Wenn ich jetzt versuche, auf beide obj1.mymin
und obj1.mymax
Attribute zuzugreifen, funktioniert alles einwandfrei. Aber wenn ich versuche, auf ein obj1.mycurrent
Attribut zuzugreifen , gibt mir PythonAttributeError: 'Count' object has no attribute 'mycurrent'
class Count():
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent) --> AttributeError: 'Count' object has no attribute 'mycurrent'
Nun meine Klasse Count hat __getattr__
Methode. Wenn ich jetzt versuche, auf obj1.mycurrent
Attribut zuzugreifen , gibt Python mir alles zurück, was ich in meiner __getattr__
Methode implementiert habe . In meinem Beispiel erstellt Python dieses Attribut, wenn ich versuche, ein nicht vorhandenes Attribut aufzurufen, und setzt es auf den ganzzahligen Wert 0.
class Count:
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
def __getattr__(self, item):
self.__dict__[item]=0
return 0
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent1)
__getattribute__
Nun sehen wir uns die __getattribute__
Methode an. Wenn Sie eine __getattribute__
Methode in Ihrer Klasse haben, ruft Python diese Methode für jedes Attribut auf, unabhängig davon, ob es vorhanden ist oder nicht. Warum brauchen wir also eine __getattribute__
Methode? Ein guter Grund ist, dass Sie den Zugriff auf Attribute verhindern und sie sicherer machen können, wie im folgenden Beispiel gezeigt.
Immer wenn jemand versucht, auf meine Attribute zuzugreifen, die mit der Teilzeichenfolge 'cur' Python beginnen, wird eine AttributeError
Ausnahme ausgelöst . Andernfalls wird dieses Attribut zurückgegeben.
class Count:
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
self.current=None
def __getattribute__(self, item):
if item.startswith('cur'):
raise AttributeError
return object.__getattribute__(self,item)
# or you can use ---return super().__getattribute__(item)
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
Wichtig: Um eine unendliche Rekursion in der __getattribute__
Methode zu vermeiden , sollte ihre Implementierung immer die Basisklassenmethode mit demselben Namen aufrufen, um auf alle benötigten Attribute zuzugreifen. Zum Beispiel: object.__getattribute__(self, name)
oder super().__getattribute__(item)
und nichtself.__dict__[item]
Wenn Ihre Klasse sowohl die magischen Methoden getattr als auch getattribute enthält , __getattribute__
wird sie zuerst aufgerufen. Aber wenn __getattribute__
Raises
AttributeError
Ausnahme dann wird die Ausnahme ignoriert und __getattr__
Methode wird aufgerufen. Siehe folgendes Beispiel:
class Count(object):
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
self.current=None
def __getattr__(self, item):
self.__dict__[item]=0
return 0
def __getattribute__(self, item):
if item.startswith('cur'):
raise AttributeError
return object.__getattribute__(self,item)
# or you can use ---return super().__getattribute__(item)
# note this class subclass object
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
__getattribute__
aber das ist es sicherlich nicht. Denn gemäß Ihrem Beispiel lösen Sie nur __getattribute__
eine AttributeError
Ausnahme aus, wenn das Attribut im __dict__
Objekt nicht vorhanden ist. Dies ist jedoch nicht unbedingt erforderlich, da dies die Standardimplementierung von ist __getattribute__
und infact __getattr__
genau das ist, was Sie als Fallback-Mechanismus benötigen.
current
auf Instanzen definiert Count
(siehe __init__
), die Erhöhung so einfach , AttributeError
wenn das Attribut ist nicht , ist nicht ganz das, was passiert ist - es aufschiebt __getattr__
für alle Namen ‚cur‘ beginnen, einschließlich current
, aber auch curious
, curly
...
Dies ist nur ein Beispiel, das auf der Erklärung von Ned Batchelder basiert .
__getattr__
Beispiel:
class Foo(object):
def __getattr__(self, attr):
print "looking up", attr
value = 42
self.__dict__[attr] = value
return value
f = Foo()
print f.x
#output >>> looking up x 42
f.x = 3
print f.x
#output >>> 3
print ('__getattr__ sets a default value if undefeined OR __getattr__ to define how to handle attributes that are not found')
Und wenn das gleiche Beispiel mit verwendet wird, erhalten __getattribute__
Sie >>>RuntimeError: maximum recursion depth exceeded while calling a Python object
__getattr__()
Implementierungen wird nur eine begrenzte Anzahl gültiger Attributnamen akzeptiert, indem AttributeError
ungültige Attributnamen ausgelöst werden, wodurch subtile und schwer zu debuggende Probleme vermieden werden . In diesem Beispiel werden alle Attributnamen unbedingt als gültig akzeptiert - ein bizarrer (und offen gesagt fehleranfälliger) Missbrauch von __getattr__()
. Wenn Sie wie in diesem Beispiel die vollständige Kontrolle über die Attributerstellung wünschen, möchten Sie __getattribute__()
stattdessen.
defaultdict
.
__getattr__
es vor der Suche nach Superklassen aufgerufen wird . Dies ist für eine direkte Unterklasse von in Ordnung object
, da die einzigen Methoden, die Sie wirklich interessieren, magische Methoden sind, die die Instanz ohnehin ignorieren. Bei einer komplexeren Vererbungsstruktur entfernen Sie jedoch vollständig die Möglichkeit, etwas vom übergeordneten Element zu erben.
Klassen object
neuen Stils erben von oder von einer anderen neuen Stilklasse:
class SomeObject(object):
pass
class SubObject(SomeObject):
pass
Old-Style-Klassen nicht:
class SomeObject:
pass
Dies gilt nur für Python 2 - in Python 3 werden mit allen oben genannten Methoden Klassen neuen Stils erstellt.
Siehe 9. Klassen (Python-Tutorial), NewClassVsClassicClass und Was ist der Unterschied zwischen alten und neuen Stilklassen in Python? für Details.
Klassen neuen Stils sind solche, die "Objekt" (direkt oder indirekt) unterordnen. Sie haben __new__
zusätzlich zu __init__
und etwas rationaleres Verhalten auf niedriger Ebene eine Klassenmethode .
Normalerweise möchten Sie überschreiben __getattr__
(wenn Sie dies auch überschreiben), andernfalls fällt es Ihnen schwer, die Syntax "self.foo" in Ihren Methoden zu unterstützen.
Zusätzliche Informationen: http://www.devx.com/opensource/Article/31482/0/page/4
Beim Lesen von Beazley & Jones PCB bin ich auf einen expliziten und praktischen Anwendungsfall gestoßen __getattr__
, der bei der Beantwortung des "Wann" -Teils der OP-Frage hilft. Von dem Buch:
"Die __getattr__()
Methode ähnelt einem Sammelbegriff für die Attributsuche. Diese Methode wird aufgerufen, wenn Code versucht, auf ein nicht vorhandenes Attribut zuzugreifen." Wir wissen dies aus den obigen Antworten, aber in PCB-Rezept 8.15 wird diese Funktionalität verwendet, um das Delegationsdesignmuster zu implementieren . Wenn Objekt A ein Attribut Objekt B hat, das viele Methoden implementiert, an die Objekt A delegieren möchte, anstatt alle Methoden von Objekt B in Objekt A neu zu definieren, nur um die Methoden von Objekt B aufzurufen, definieren Sie eine __getattr__()
Methode wie folgt:
def __getattr__(self, name):
return getattr(self._b, name)
Dabei ist _b der Name des Attributs von Objekt A, das ein Objekt B ist. Wenn eine für Objekt B definierte Methode für Objekt A aufgerufen wird, wird die __getattr__
Methode am Ende der Suchkette aufgerufen. Dies würde auch den Code sauberer machen, da Sie keine Liste von Methoden definiert haben, die nur zum Delegieren an ein anderes Objekt definiert sind.