2017-02-02 2 views
1

Also möchte ich eine Klasse aktualisieren, die in einer Bibliothek ist, aber ich habe keine Kontrolle über diese Klasse (ich kann den ursprünglichen Quellcode nicht berühren). Einschränkungsnummer 2: Andere Benutzer haben diese Elternklasse bereits geerbt, und die Frage, ob sie von einer dritten Klasse erben sollen, wäre ein bisschen "ärgerlich". Also muss ich mit beiden Constraints gleichzeitig arbeiten: Ich muss die Elternklasse erweitern, aber nicht, indem ich sie vererbe.Dynamisches dekorieren mit statischen Dekoratoren

Eine Lösung schien zuerst sinnvoller zu sein, obwohl sie an "Affepatching" grenzt. Überschreiben einiger Methoden der übergeordneten Klasse durch meine eigenen. Ich habe einen kleinen Dekorateur geschrieben, der das machen könnte. Aber ich habe einen Fehler entdeckt, und anstatt Ihnen den GESAMTEN Code zu geben, hier ein Beispiel. Bedenken Sie, dass die folgende Klasse, genannt Old hier (die Elternklasse), die in einer Bibliothek, die ich nicht (in Bezug auf den Quellcode, sowieso) berühren kann:

class Old(object): 

    def __init__(self, value): 
     self.value = value 

    def at_disp(self, other): 
     print "Value is", self.value, other 
     return self.value 

, dass eine einfache Klasse ist, ein Konstruktor und ein Verfahren mit einem Parameter (um ein bisschen mehr zu testen). Nichts wirklich schwer bis jetzt. Aber hier kommt mein Dekorateur, um eine Methode dieser Klasse zu erweitern:

def override_hook(typeclass, method_name): 
    hook = getattr(typeclass, method_name) 
    def wrapper(method): 
     def overriden_hook(*args, **kwargs): 
      print "Before the hook is called." 
      kwargs["hook"] = hook 
      ret = method(*args, **kwargs) 
      print "After the hook" 
      return ret 
     setattr(typeclass, method_name, overriden_hook) 
     return overriden_hook 
    return wrapper 

@override_hook(Old, "at_disp") 
def new_disp(self, other, hook): 
    print "In the new hook, before" 
    ret = hook(self, other) 
    print "In the new hook, after" 
    return ret 

Überraschenderweise funktioniert das perfekt. Wenn Sie eine alte Instanz erstellen und ihre Methode at_disp aufrufen, wird die neue Methode aufgerufen (und die alte aufgerufen). Ähnlich wie versteckte Vererbung. Aber hier ist die echte Herausforderung:

Wir werden versuchen, eine Klasse von Old zu erben haben. Das haben die Nutzer gemacht. Mein „Patch“ auch für sie gelten sollte, ohne dass für sie etwas zu tun:

class New(Old): 
    def at_disp(self, other): 
     print "That's in New..." 
     return super(Old, self).at_disp(self, other) 

Wenn Sie ein neues Objekt erstellen, und versucht, seine at_disp Methode ... es stürzt ab. super() kann at_disp in Old nicht finden. Das ist seltsam, weil New direkt von Old erbt. Meine Vermutung ist, dass, da meine neue, ersetzte Methode nicht gebunden ist, super() sie nicht richtig findet. Wenn Sie super() durch einen direkten Aufruf von Old.at_disp() ersetzen, funktioniert alles.

Kann jemand dieses Problem beheben? Und warum passiert es?

Vielen Dank!

Antwort

1

Zwei Probleme.

Zuerst sollte der Aufruf von Super super(New, self) sein, nicht super(Old, self). Das erste Argument zu super ist allgemein die "aktuelle" Klasse (d. H. Die Klasse, deren Methode super aufruft).

Zweitens sollte der Aufruf an die at_disp Methode nur at_disp(other) sein, nicht at_disp(self, other). Wenn Sie das Formular mit zwei Argumenten super verwenden, erhalten Sie ein gebundenes Superobjekt, das sich wie eine Instanz verhält. Daher wird self automatisch übergeben, wenn Sie eine Methode aufrufen.

So sollte der Anruf super(New, self).at_disp(other) sein. Dann funktioniert es.

+0

Perfekt, danke! Ich schäme mich, das verpasst zu haben: S. –