2014-09-19 8 views
33

Warum tun wir __getitem__ statt der üblichen Bedienerzugriff verwenden?Warum müssen wir die __dunder__ Methoden anstelle von Operatoren verwenden, wenn Sie über Super aufrufen?

class MyDict(dict): 
    def __getitem__(self, key): 
     return super()[key] 

Wir erhalten TypeError: 'super' object is not subscriptable.

Stattdessen müssen wir super().__getitem__(key) verwenden, aber ich habe nie ganz verstanden, warum - was genau verhinderte, dass Super so implementiert wurde, dass der Bediener darauf zugreifen konnte?

Subscriptable war nur ein Beispiel, habe ich die gleiche Frage für __getattr__, __init__ usw.

Den docs Versuch zu erklären, warum, aber ich verstehe es nicht.

+0

http://stackoverflow.com/questions/12047847/super-object-not-calling-getattr –

+0

Auch hilfreich: https://docs.python.org/3/reference/datamodel.html#special-method- Nachschlagen – wim

Antwort

18

CPython Bug Tracker issue 805304, "super instances don't support item assignment", hat Raymond Hettinger eine detaillierte Erklärung der empfundenen Schwierigkeiten geben.

Der Grund, warum dies nicht automatisch funktioniert, ist, dass solche Methoden in der Klasse definiert werden müssen, weil Python Methoden zwischenspeichert, während die Proxy-Methoden zur Laufzeit gefunden werden.

Er bietet a patch die eine Teilmenge dieser Funktionalität geben würde:

+ if (o->ob_type == &PySuper_Type) { 
+  PyObject *result; 
+  result = PyObject_CallMethod(o, "__setitem__", "(OO)", key, value); 
+  if (result == NULL) 
+   return -1; 
+  Py_DECREF(result); 
+  return 0; 
+ } 
+ 

so ist es klar möglich.

Allerdings schließt er

Ich habe nachgedacht, dass dies eine allein gelassen werden kann und nur Dokument, dass Super Objekte nur auf explizites Attribut Lookup ihre Magie zu tun.

Ansonsten ist es völlig Fixierung beinhaltet jeden Ort Python Kämme, die direkt Funktionen aus dem Spieltisch ruft, für und dann ein Followup Gespräch hinzugefügt werden mit Attribut-Lookup, wenn die Slot leer ist.

Wenn es um Funktionen wie repr (obj), ich denke, wir die Super Objekt wollen sich eher identifizieren als die Aufruf an das Zielobjekt des __repr __() -Methode weiterzuleiten.

Das Argument scheint zu sein, dass, wenn __dunder__ Methoden proxied sind, dann ist entweder __repr__ proxied oder es besteht eine Inkonsistenz zwischen ihnen. super() somit vielleicht nicht wollen, solche Methoden zu Proxy, damit es nicht zu nahe an das Äquivalent eines unheimlichen Tals Programmierer bekommt.

3

Dunder Methoden müssen von der Klasse definiert werden, nicht die Instanz. super() müsste eine Implementierung jeder magischen Methode haben, damit dies funktioniert. Es lohnt sich nicht alle, dass das Schreiben von Code und up-to-date mit der Sprachdefinition (beispielsweise die Einführung von Matrix-Multiplikation in 3,5 erstellt drei neue dunder Methoden) zu halten, wenn Sie nur Benutzer sagen können die dunder Methoden von Hand zu schreiben. Das verwendet normale Methodennachschlage, die problemlos emuliert werden können.

+2

FWIW, das scheint eine Teilmenge meiner (jetzt selbst gelöschten) Antwort zu sein. Das Kopfgeld sagt "die aktuelle Antwort ist nicht überzeugend für mich", also wird dies wahrscheinlich auch nicht sein. – Veedrac

3

Was man fragen kann, und einfach durchgeführt werden.Zum Beispiel:

Also der Grund, dass super mit magischen Methoden arbeitet, ist nicht, weil es nicht möglich ist. Der Grund muss woanders liegen. Ein Grund ist, dass dies den Vertrag von Gleichgestellten verletzen würde (==). Das Gleiche ist unter anderem symmetrisch. Das heißt, wenn a == b wahr ist, muss auch b == a wahr sein. Das bringt uns in eine knifflige Situation, wo super(self, CurrentClass) == self, aber self != super(self, CurrentClass) z.

class dundersuper(super): 
    def __eq__(self, other): 
     return self.__eq__(other) 

super = dundersuper 

class A: 
    def self_is_other(self, other): 
     return super() == other # a.k.a. object.__eq__(self, other) or self is other 
    def __eq__(self, other): 
     """equal if both of type A""" 
     return A is type(self) and A is type(other) 

class B: 
    def self_is_other(self, other): 
     return other == super() # a.k.a object.__eq__(other, super()), ie. False 
    def __eq__(self, other): 
     return B is type(self) and B is type(other) 

assert A() == A() 
a = A() 
assert a.self_is_other(a) 
assert B() == B() 
b = B() 
assert b.self_is_other(b) # assertion fails 

Ein weiterer Grund ist, dass, sobald Super getan sucht es das Objekt MRO gegeben hat, es dann zu geben, hat sich eine Chance, die gewünschte Attribut zu liefern - Super-Objekte sind nach wie vor ein Objekte in ihrem eigenen Recht - wir sollten Sie können nach Gleichheit mit anderen Objekten suchen, nach Zeichenkettenrepräsentationen fragen und das Objekt und die Klasse untersuchen, mit denen Super arbeitet. Dies führt zu einem Problem, wenn die Methode dunder für das Superobjekt verfügbar ist, nicht jedoch für das Objekt, das das veränderbare Objekt darstellt. Zum Beispiel:

class dundersuper(super): 
    def __add__(self, other): 
     return self.__add__(other) 
    def __iadd__(self, other): 
     return self.__iadd__(other) 

super = dundersuper 

class MyDoubleList(list): 
    """Working, but clunky example.""" 
    def __add__(self, other): 
     return MyDoubleList(super() + 2 * other) 
    def __iadd__(self, other): 
     s = super() 
     s += 2 * other # can't assign to the result of a function, so we must assign 
     # the super object to a local variable first 
     return s 

class MyDoubleTuple(tuple): 
    """Broken example -- iadd creates infinite recursion""" 
    def __add__(self, other): 
     return MyDoubleTuple(super() + 2 * other) 
    def __iadd__(self, other): 
     s = super() 
     s += 2 * other 
     return s 

Mit der Liste Beispiel __iadd__ die Funktion mehr haben könnte einfach als

def __iadd__(self, other): 
    return super().__iadd__(other) 

Mit dem Tupel Beispiel geschrieben uns in unendliche Rekursion fallen, ist dies, weil tuple.__iadd__ existiert nicht. Daher wird beim Nachschlagen des Attributs __iadd__ an einem Super-Objekt das tatsächliche Super-Objekt auf ein __iadd__ Attribut überprüft (das existiert). Wir bekommen diese Methode und rufen sie auf, wodurch der gesamte Prozess erneut gestartet wird. Wenn wir keine __iadd__ Methode auf Super geschrieben hätten und super().__iadd__(other) benutzt hätten, wäre das niemals passiert. Stattdessen erhalten wir eine Fehlermeldung über ein Super-Objekt ohne das Attribut __iadd__. Etwas kryptisch, aber weniger als eine unendliche Stapelspur.

Der Grund, warum Super funktioniert nicht mit magischen Methoden ist, dass es mehr Probleme schafft, als es löst.

Verwandte Themen