Ich habe gelesen, dass es möglich ist, einem vorhandenen Objekt (dh nicht in der Klassendefinition) in Python eine Methode hinzuzufügen.
Ich verstehe, dass es nicht immer gut ist, dies zu tun. Aber wie könnte man das machen?
quelle
Ich habe gelesen, dass es möglich ist, einem vorhandenen Objekt (dh nicht in der Klassendefinition) in Python eine Methode hinzuzufügen.
Ich verstehe, dass es nicht immer gut ist, dies zu tun. Aber wie könnte man das machen?
In Python gibt es einen Unterschied zwischen Funktionen und gebundenen Methoden.
>>> def foo():
... print "foo"
...
>>> class A:
... def bar( self ):
... print "bar"
...
>>> a = A()
>>> foo
<function foo at 0x00A98D70>
>>> a.bar
<bound method A.bar of <__main__.A instance at 0x00A9BC88>>
>>>
Gebundene Methoden wurden an eine Instanz "gebunden" (wie beschreibend), und diese Instanz wird bei jedem Aufruf der Methode als erstes Argument übergeben.
Callables, die Attribute einer Klasse sind (im Gegensatz zu einer Instanz), sind jedoch noch nicht gebunden, sodass Sie die Klassendefinition jederzeit ändern können:
>>> def fooFighters( self ):
... print "fooFighters"
...
>>> A.fooFighters = fooFighters
>>> a2 = A()
>>> a2.fooFighters
<bound method A.fooFighters of <__main__.A instance at 0x00A9BEB8>>
>>> a2.fooFighters()
fooFighters
Zuvor definierte Instanzen werden ebenfalls aktualisiert (sofern sie das Attribut selbst nicht überschrieben haben):
>>> a.fooFighters()
fooFighters
Das Problem tritt auf, wenn Sie eine Methode an eine einzelne Instanz anhängen möchten:
>>> def barFighters( self ):
... print "barFighters"
...
>>> a.barFighters = barFighters
>>> a.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: barFighters() takes exactly 1 argument (0 given)
Die Funktion wird nicht automatisch gebunden, wenn sie direkt an eine Instanz angehängt wird:
>>> a.barFighters
<function barFighters at 0x00A98EF0>
Um es zu binden, können wir die MethodType-Funktion im Typmodul verwenden :
>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters
Diesmal sind andere Instanzen der Klasse nicht betroffen:
>>> a2.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'
Weitere Informationen können durch das Lesen über finden Deskriptoren und Metaklasse Programmierung .
MethodType
, rufen Sie das Deskriptorprotokoll manuell auf und lassen Sie die Funktion Ihre Instanz erstellen:barFighters.__get__(a)
Erzeugt eine gebundene Methode für diebarFighters
Bindung ana
.descriptor protocol
vs erstellen eineMethodType
Seite, vielleicht ein wenig besser lesbar zu sein.classmethod
undstaticmethod
und andere Deskriptoren funktionieren . Dadurch wird vermieden, dass der Namespace mit einem weiteren Import überladen wird.a.barFighters = barFighters.__get__(a)
Das neue Modul ist seit Python 2.6 veraltet und in 3.0 entfernt. Verwenden Sie Typen
Siehe http://docs.python.org/library/new.html
Im folgenden Beispiel habe ich den Rückgabewert absichtlich aus der
patch_me()
Funktion entfernt. Ich denke, dass die Angabe des Rückgabewerts den Eindruck erwecken kann, dass der Patch ein neues Objekt zurückgibt, was nicht der Fall ist - es ändert das eingehende Objekt. Wahrscheinlich kann dies einen disziplinierteren Einsatz von Monkeypatching ermöglichen.quelle
Vorwort - ein Hinweis zur Kompatibilität: Andere Antworten funktionieren möglicherweise nur in Python 2 - diese Antwort sollte in Python 2 und 3 einwandfrei funktionieren. Wenn Sie nur Python 3 schreiben, können Sie das explizite Erben von auslassen
object
, ansonsten sollte der Code derselbe bleiben .Ja, es ist möglich - aber nicht zu empfehlen
Ich empfehle das nicht. Das ist eine schlechte Idee. Tu es nicht.
Hier sind einige Gründe:
Daher schlage ich vor, dass Sie dies nur tun, wenn Sie einen wirklich guten Grund haben. Es ist weitaus besser, die richtige Methode in der Klassendefinition zu definieren oder weniger bevorzugt, die Klasse direkt wie folgt zu patchen:
Da es jedoch lehrreich ist, werde ich Ihnen einige Möglichkeiten zeigen, dies zu tun.
Wie es geht
Hier ist ein Setup-Code. Wir brauchen eine Klassendefinition. Es könnte importiert werden, aber es spielt wirklich keine Rolle.
Erstellen Sie eine Instanz:
Erstellen Sie eine Methode, um sie hinzuzufügen:
Methode nichts (0) - benutze die Deskriptormethode,
__get__
Gepunktete Suchvorgänge für Funktionen rufen die
__get__
Methode der Funktion mit der Instanz auf, binden das Objekt an die Methode und erstellen so eine "gebundene Methode".und nun:
Methode eins - types.MethodType
Importieren Sie zunächst die Typen, von denen wir den Methodenkonstruktor erhalten:
Jetzt fügen wir die Methode der Instanz hinzu. Dazu benötigen wir den MethodType-Konstruktor aus dem
types
Modul (das wir oben importiert haben).Die Argument-Signatur für types.MethodType lautet
(function, instance, class)
:und Verwendung:
Methode zwei: lexikalische Bindung
Zuerst erstellen wir eine Wrapper-Funktion, die die Methode an die Instanz bindet:
Verwendungszweck:
Methode drei: functools.partial
Eine Teilfunktion wendet die ersten Argumente auf eine Funktion (und optional auf Schlüsselwortargumente) an und kann später mit den verbleibenden Argumenten (und überschreibenden Schlüsselwortargumenten) aufgerufen werden. Somit:
Dies ist sinnvoll, wenn Sie bedenken, dass gebundene Methoden Teilfunktionen der Instanz sind.
Ungebundene Funktion als Objektattribut - warum das nicht funktioniert:
Wenn wir versuchen, die sample_method auf die gleiche Weise hinzuzufügen, wie wir sie möglicherweise zur Klasse hinzufügen, ist sie nicht an die Instanz gebunden und nimmt das implizite Selbst nicht als erstes Argument.
Wir können die ungebundene Funktion zum Funktionieren bringen, indem wir die Instanz explizit übergeben (oder irgendetwas anderes, da diese Methode die
self
Argumentvariable nicht verwendet ), aber sie würde nicht mit der erwarteten Signatur anderer Instanzen übereinstimmen (wenn wir Affen-Patches durchführen) diese Instanz):Fazit
Sie kennen jetzt verschiedene Möglichkeiten, wie Sie dies tun können, aber im Ernst - tun Sie dies nicht.
quelle
__get__
Methode benötigt auch die Klasse als nächsten Parameter :sample_method.__get__(foo, Foo)
.Ich denke, dass die obigen Antworten den entscheidenden Punkt verfehlt haben.
Lassen Sie uns eine Klasse mit einer Methode haben:
Lassen Sie uns jetzt in ipython damit spielen:
Ok, so m () wird irgendwie ein ungebundenes Methode A . Aber ist es wirklich so?
Es stellt sich heraus, dass m () nur eine Funktion ist, deren Verweis zu einem Klassenwörterbuch hinzugefügt wird - es gibt keine Magie. Warum gibt uns Am dann eine ungebundene Methode? Dies liegt daran, dass der Punkt nicht in eine einfache Wörterbuchsuche übersetzt wird. Es ist de facto ein Aufruf von A .__ Klasse __.__ getattribute __ (A, 'm'):
Jetzt bin ich mir nicht sicher, warum die letzte Zeile zweimal gedruckt wird, aber es ist immer noch klar, was dort vor sich geht.
Das Standard-__getattribute__ überprüft nun, ob das Attribut ein sogenannter Deskriptor ist oder nicht, dh ob es eine spezielle __get__- Methode implementiert. Wenn diese Methode implementiert wird, wird das Ergebnis des Aufrufs dieser __get__ -Methode zurückgegeben. Wir kehren zur ersten Version unserer A- Klasse zurück und haben Folgendes:
Und da Python-Funktionen das Deskriptorprotokoll implementieren, binden sie sich in ihrer __get__ -Methode an dieses Objekt, wenn sie im Namen eines Objekts aufgerufen werden.
Ok, wie fügt man einem vorhandenen Objekt eine Methode hinzu? Angenommen, es macht Ihnen nichts aus, die Klasse zu patchen, ist es so einfach wie:
Dann "wird" Bm dank der Deskriptormagie zu einer ungebundenen Methode.
Wenn Sie eine Methode nur einem einzelnen Objekt hinzufügen möchten, müssen Sie die Maschinerie mithilfe von types selbst emulieren. MethodType:
Apropos:
quelle
In Python funktioniert das Patchen von Affen im Allgemeinen durch Überschreiben einer Klassen- oder Funktionssignatur mit Ihrer eigenen. Unten ist ein Beispiel aus dem Zope-Wiki :
Dieser Code überschreibt / erstellt eine Methode namens speak für die Klasse. In Jeff Atwoods jüngstem Beitrag über das Patchen von Affen . Er zeigt ein Beispiel in C # 3.0, der aktuellen Sprache, die ich für die Arbeit verwende.
quelle
Sie können Lambda verwenden, um eine Methode an eine Instanz zu binden:
Ausgabe:
quelle
Es gibt mindestens zwei Möglichkeiten, eine Methode an eine Instanz anzuhängen, ohne
types.MethodType
:1:
2:
Nützliche Links:
Datenmodell - Aufrufen von Deskriptoren
Descriptor HowTo Guide - Aufrufen von Deskriptoren
quelle
Was Sie suchen,
setattr
glaube ich. Verwenden Sie diese Option, um ein Attribut für ein Objekt festzulegen.quelle
A
, nicht die Instanza
.setattr(A,'printme',printme)
statt einfach zu verwendenA.printme = printme
?Da diese Frage für Nicht-Python-Versionen gestellt wurde, ist hier JavaScript:
quelle
Konsolidierung der Antworten von Jason Pratt und des Community-Wikis mit Blick auf die Ergebnisse verschiedener Bindungsmethoden:
Beachten Sie insbesondere, wie das Hinzufügen der Bindungsfunktion als Klassenmethode funktioniert , der Referenzierungsbereich jedoch falsch ist.
Persönlich bevorzuge ich die externe ADDMETHOD-Funktionsroute, da ich damit auch innerhalb eines Iterators dynamisch neue Methodennamen zuweisen kann.
quelle
addmethod
neu geschrieben auf folgende Weisedef addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )
löst das ProblemDies ist eigentlich ein Addon zur Antwort von "Jason Pratt"
Obwohl Jasons Antwort funktioniert, funktioniert es nur, wenn man einer Klasse eine Funktion hinzufügen möchte. Es hat bei mir nicht funktioniert, als ich versucht habe, eine bereits vorhandene Methode aus der .py-Quellcodedatei neu zu laden.
Ich habe ewig gebraucht, um eine Problemumgehung zu finden, aber der Trick scheint einfach zu sein ... 1. Importieren Sie den Code aus der Quellcodedatei. 2. Erzwingen Sie ein erneutes Laden. 3. Verwenden Sie types.FunctionType (...), um den zu konvertieren importierte und gebundene Methode an eine Funktion Sie können auch die aktuellen globalen Variablen weitergeben, da sich die neu geladene Methode in einem anderen Namespace befindet. 4. Jetzt können Sie wie von "Jason Pratt" vorgeschlagen mit den types.MethodType (... )
Beispiel:
quelle
Wenn es hilfreich sein kann, habe ich kürzlich eine Python-Bibliothek namens Gorilla veröffentlicht, um das Patchen von Affen bequemer zu gestalten.
Die Verwendung einer Funktion
needle()
zum Patchen eines Moduls mit dem Namenguineapig
lautet wie folgt:Es werden aber auch interessantere Anwendungsfälle behandelt, wie in den FAQ aus der Dokumentation gezeigt .
Der Code ist auf GitHub verfügbar .
quelle
Diese Frage wurde vor Jahren gestellt, aber hey, es gibt eine einfache Möglichkeit, die Bindung einer Funktion an eine Klasseninstanz mithilfe von Dekoratoren zu simulieren:
Wenn Sie dort die Funktion und die Instanz an den Ordnerdekorateur übergeben, wird dort eine neue Funktion mit demselben Codeobjekt wie das erste erstellt. Anschließend wird die angegebene Instanz der Klasse in einem Attribut der neu erstellten Funktion gespeichert. Der Dekorateur gibt eine (dritte) Funktion zurück, die automatisch die kopierte Funktion aufruft und die Instanz als ersten Parameter angibt.
Abschließend erhalten Sie eine Funktion, die simuliert, dass sie an die Klasseninstanz gebunden ist. Die ursprüngliche Funktion bleibt unverändert.
quelle
Was Jason Pratt gepostet hat, ist richtig.
Wie Sie sehen können, betrachtet Python b () nicht anders als a (). In Python sind alle Methoden nur Variablen, die zufällig Funktionen sind.
quelle
Test
, keine Instanz davon.Ich finde es seltsam, dass niemand erwähnt hat, dass alle oben aufgeführten Methoden eine Zyklusreferenz zwischen der hinzugefügten Methode und der Instanz erstellen, wodurch das Objekt bis zur Speicherbereinigung persistent bleibt. Es gab einen alten Trick beim Hinzufügen eines Deskriptors durch Erweitern der Klasse des Objekts:
quelle
Hiermit können Sie den Selbstzeiger verwenden
quelle