2015-03-04 6 views
9

Wie __getattr__ mit Python 3 und Vererbung überschrieben werden?'Super' Objekt hat kein Attribut '__getattr__' in python3

Wenn ich folgendes:

class MixinA: 
    def __getattr__(self, item): 
     # Process item and return value if known 
     if item == 'a': 
      return 'MixinA' 

     # If it is unknown, pass it along to give 
     # a chance to another class to handle it 
     return super().__getattr__(item) 

class MixinB: 
    def __getattr__(self, item): 
     # Process item and return value if known 
     if item == 'b': 
      return 'MixinB' 

     # If it is unknown, pass it along to give 
     # a chance to another class to handle it 
     return super().__getattr__(item) 

class Example(MixinA, MixinB): 
    # main class 
    pass 

ich diesen Fehler.

>>> e = Example() 
>>> e.a 
'MixinA' 
>>> e.b 
'MixinB' 
>>> e.c 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
... 
AttributeError: 'super' object has no attribute '__getattr__' 

Konnte ich nicht nur den Attributfehler erhalten, der auf die ursprüngliche Klasse und Eigenschaft verweist? Das heißt:

AttributeError: 'Example' object has no attribute 'c' 

PS: Ich fand diesen Beitrag 'super' object not calling __getattr__ aber ich bin nicht sicher, zu verstehen, ob es eine Lösung gibt.

+0

Dies ist das Aushängeschild für die Verwendung von 'Eigenschaften'. – dursk

+0

@mattm Was meinst du? Könnten Sie expliziter sein? Ich möchte keine Eigenschaften verwenden. Lassen Sie mich versuchen, mehr zu erklären, was ich zu erreichen versuche. Ich erstelle mehrere Mixins, um dynamisch zusätzliche Optionen für einige Attribute basierend auf ihrem Namen hinzuzufügen. Zum Beispiel, wenn ein Attr endet mit '_logs', dann können Sie einen Auszug erhalten, indem Sie' __excerpt' => zB hinzufügen: Wenn ein Obj ein 'xxx_logs' Attribut hat, würde' obj.xxx_logs__excerpt' abgefangen und würde funktionieren. – Michael

Antwort

2

wenn Sie das tun wollen Dinge mit Mixin-Style-Klassen müssen Sie dann erstellen base-attribute-getting-class, die immer eine AttributeError auslöst.

class AttrGetter: 
    def __getattr__(self, item): 
     raise AttributeError(item) 

class MixinA(AttrGetter): 
    def __getattr__(self, item): 
     if item == 'a': 
      return 'MixinA' 
     return super().__getattr__(item) 

class MixinB(AttrGetter): 
    def __getattr__(self, item): 
     if item == 'b': 
      return 'MixinB' 
     return super().__getattr__(item) 

class Example(MixinA, MixinB): 
    pass 

# mro stands for method resolution order. 
# It defines the order in which classes are searched for attributes. 
# We're looking for __getattr__ in this circumstance. 
print(Example.mro()) 
# [<class '__main__.Example'>, <class '__main__.MixinA'>, <class '__main__.MixinB'>, 
# <class '__main__.AttrGetter'>, <class 'object'>] 
# As you can see, searches for __getattr__ only check AttrGetter after having checked 
# both mixins first. 

e = Example() 
print(e.a) 
print(e.b) 
print(e.c) 

Eigenschaften können Sie jedoch in viel einfacher und prägnanter Weise tun.

4

Der Abrufmechanismus für Python-Attribute funktioniert so, dass eine Klasse __getattr__ als "letzte Ressource" aufgerufen wird, um ein Attribut für eine Instanz dieser Klasse zu erhalten.

Ihr Code ist richtig - es schlägt aufgrund Ihrer Oberklasse von "Beispiel" - in diesem Fall "Objekt" - kein __getattr__ Attribut. Wenn Sie sich nicht in einer tiefen Klassenhierarchie befinden und einen benutzerdefinierten Attributabruf für eine direkt vom Objekt vererbte Klasse durchführen wollen (was in Py3 ausgedrückt werden kann als hätte es überhaupt keine Basen wie in Ihrem Code), dann raisen Sie einfach "AttributeError "Wenn Ihre benutzerdefinierte Suche fehlgeschlagen ist.

Wenn Ihre Absicht stattdessen ist, dass Ihre benutzerdefinierte Suche Priorität vor Pythons normalem Attribut-Retreival-Mechanismus hat (und nicht als Fallback bezeichnet wird), sollten Sie __getattribute__ anstelle von __getattr__ implementieren.

In diesem Fall ist die Basisklasse - Objekt - eine __getattribute__ Methode, die Sie für den normalen Attribut Retrieval aufrufen müssen hat - das Problem ist, dass man es für alles nennen haben Sie wollen - einschließlich Methodennamen und bekannten Attribute, die Sie festgelegt haben. d.h .: etwas zusammen:

class Example: 

    def __init__(self): 
     self.attrs_to_override = ["attr1", "foo", "bar"] 

    def docustomstuff(self, attr): 
     ... 

    def __getattribute__(self, attr): 
     getter = super().__getattribute__ 
     if attr in getter("attrs_to_override"): 
      getter("docustomstuff")(attr) 
     return getter(attr) 

Kurz gesagt, wenn Sie denken, sollten Sie __getattribute__ statt __getattr__ implementieren Sie wahrscheinlich den falschen Ansatz versuchen, außer in ganz bestimmten Fällen. Was Sie wahrscheinlich bis jetzt nicht wussten ist, dass: __getattr__ nicht aufgerufen worden wäre, wenn ein gewöhnliches Attribut durch den gewollten Namen nicht bereits existiert, so dass es nicht notwendig ist, es in der Oberklasse aufzurufen (außer die Oberklasse in case ist) nicht widersprechen und hat für sie eine bekannte customization)

bearbeiten

Alternativ nur prüfen, ob die nächste Super __getattr__ in den meisten Normal Art und Weise haben Sie:

... 
if hasattr(super(), "__getattr__"): 
    return super().__getattr__(attr) 
raise AttributeError 
+0

Vielen Dank für Ihre Antwort. Ja, ich denke, ich verstehe den Unterschied zwischen "__getattr__" und "__getattribute__" und ich meine "__getattr__". Ich möchte 'AttributeError' nicht aufrufen, weil ich mehrere Mixins haben möchte, die die' __getattr__' Methode implementieren und ich möchte, dass sie alle die Möglichkeit haben, sie abzufangen. Ich habe meinen Beitrag bearbeitet, um dieses Beispiel zu zeigen. Lass mich wissen was du denkst. Grundsätzlich bekomme ich schon den 'AttributError', aber ich möchte, dass er auf der höheren Klasse und Eigenschaft statt auf dem' Super' steht. Irgendeine Idee? – Michael

+0

Dort - ich habe ein Beispiel hinzugefügt, wie man es mit '__getattr__' macht – jsbueno