Werfen Sie einen Blick auf den folgenden Code:Wie auf die Klassenattribute einer Superklasse in Python zugreifen?
class A(object):
defaults = {'a': 1}
def __getattr__(self, name):
print('A.__getattr__')
return self.get_default(name)
@classmethod
def get_default(cls, name):
# some debug output
print('A.get_default({}) - {}'.format(name, cls))
try:
print(super(cls, cls).defaults) # as expected
except AttributeError: #except for the base object class, of course
pass
# the actual function body
try:
return cls.defaults[name]
except KeyError:
return super(cls, cls).get_default(name) # infinite recursion
#return cls.__mro__[1].get_default(name) # this works, though
class B(A):
defaults = {'b': 2}
class C(B):
defaults = {'c': 3}
c = C()
print('c.a =', c.a)
Ich habe eine Hierarchie von Klassen jeweils mit einem eigenen Wörterbuch einige Standardwerte enthält. Wenn eine Instanz einer Klasse kein bestimmtes Attribut aufweist, sollte stattdessen ein Standardwert für diese Klasse zurückgegeben werden. Wenn in dem defaults
Wörterbuch der aktuellen Klasse kein Standardwert für das Attribut enthalten ist, sollte das defaults
Wörterbuch der Oberklasse durchsucht werden.
Ich versuche, dies mit der rekursiven Klassenmethode get_default
zu implementieren. Das Programm bleibt leider in einer unendlichen Rekursion stecken. Mein Verständnis von super()
fehlt offensichtlich. Wenn ich auf __mro__
zugreife, kann ich zwar richtig funktionieren, aber ich bin mir nicht sicher, ob das eine richtige Lösung ist.
Ich habe das Gefühl, die Antwort ist irgendwo in this article, aber ich konnte es noch nicht finden. Vielleicht muss ich auf eine Metaklasse zurückgreifen?
bearbeiten: In meiner Anwendung überprüft __getattr__
zuerst self.base
. Wenn es nicht None
ist, muss das Attribut von dort abgerufen werden. Nur im anderen Fall muss ein Standardwert zurückgegeben werden. Ich könnte wahrscheinlich __getattribute__
überschreiben. Wäre das die bessere Lösung?
bearbeiten 2: Unten ist ein erweitertes Beispiel für die Funktionalität, die ich suche. Es wird derzeit unter Verwendung von __mro__
implementiert (unutbus früherer Vorschlag, im Gegensatz zu meiner ursprünglichen rekursiven Methode). Wenn nicht jemand eine elegantere Lösung vorschlagen kann, bin ich glücklich, diese Implementierung zu verwenden. Ich hoffe, das klärt die Dinge auf.
class A(object):
defaults = {'a': 1}
def __init__(self, name, base=None):
self.name = name
self.base = base
def __repr__(self):
return self.name
def __getattr__(self, name):
print(" '{}' attribute not present in '{}'".format(name, self))
if self.base is not None:
print(" getting '{}' from base ({})".format(name, self.base))
return getattr(self.base, name)
else:
print(" base = None; returning default value")
return self.get_default(name)
def get_default(self, name):
for cls in self.__class__.__mro__:
try:
return cls.defaults[name]
except KeyError:
pass
raise KeyError
class B(A):
defaults = {'b': 2}
class C(B):
defaults = {'c': 3}
c1 = C('c1')
c1.b = 55
print('c1.a = ...'); print(' ...', c1.a) # 1
print(); print('c1.b = ...'); print(' ...', c1.b) # 55
print(); print('c1.c = ...'); print(' ...', c1.c) # 3
c2 = C('c2', base=c1)
c2.c = 99
print(); print('c2.a = ...'); print(' ...', c2.a) # 1
print(); print('c2.b = ...'); print(' ...', c2.b) # 55
print(); print('c2.c = ...'); print(' ...', c2.c) # 99
Der Ausgang:
c1.a = ...
'a' attribute not present in 'c1'
base = None; returning default value
... 1
c1.b = ...
... 55
c1.c = ...
'c' attribute not present in 'c1'
base = None; returning default value
... 3
c2.a = ...
'a' attribute not present in 'c2'
getting 'a' from base (c1)
'a' attribute not present in 'c1'
base = None; returning default value
... 1
c2.b = ...
'b' attribute not present in 'c2'
getting 'b' from base (c1)
... 55
c2.c = ...
... 99
Wie wäre es, die Standardwerte direkt in den Klassennamespace zu platzieren, anstatt das Wörterbuch 'defaults' zu verwenden? Das würde jegliche Magie unnötig machen - es würde einfach funktionieren. –
@Sven Ich wusste, dass ich das hätte erwähnen sollen :) In meiner Anwendung prüft der '__getattr__' zuerst ein anderes Attribut (Basis). Nur wenn das andere Attribut None ist, sollte der Standardwert zurückgegeben werden. Andernfalls muss das Attribut in der Basis gesucht werden. –
Ja, so funktionieren Instanz-/Klassenattribute in Python. :) Überprüfen Sie Tonis Antwort. Afaik wird seinen Code genauso machen wie deiner. –