Ich habe gelesen, dass es möglich ist, eine Methode zu einem bestehenden Objekt (dh nicht in der Klassendefinition) in Python hinzuzufügen.
Ich verstehe, dass es nicht immer gut ist, dies zu tun. Aber wie könnte man das tun?
Akdom
Ich habe gelesen, dass es möglich ist, eine Methode zu einem bestehenden Objekt (dh nicht in der Klassendefinition) in Python hinzuzufügen.
Ich verstehe, dass es nicht immer gut ist, dies zu tun. Aber wie könnte man das tun?
Jason Pratt
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 als erstes Argument übergeben, wenn die Methode aufgerufen wird.
Callables, die Attribute einer Klasse sind (im Gegensatz zu einer Instanz), sind jedoch immer noch ungebunden, 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 nicht selbst ü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 verwenden MethodType-Funktion im Typenmodul:
>>> 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 finden Sie unter Lesen über Beschreibungen Und Metaklasse Programmierung.
Anstatt manuell eine zu erstellen MethodType
rufen Sie die auf Deskriptor-Protokoll manuell und lassen Sie die Funktion Ihre Instanz erzeugen: barFighters.__get__(a)
erzeugt eine gebundene Methode für barFighters
gebunden an a
.
– Martijn Pieters
♦
16. September 2016 um 13:13 Uhr
@MartijnPieters alle Vorteile der Verwendung der descriptor protocol
vs Erstellen von a MethodType
Abgesehen davon, dass es vielleicht etwas besser lesbar ist.
– EndermanAPM
10. Mai 2017 um 7:57 Uhr
@EndermanAPM: Mehrere: Es ist wahrscheinlicher, dass es weiterhin genauso funktioniert wie der Zugriff auf das Attribut in einer Instanz. Es wird funktionieren classmethod
Und staticmethod
und andere Deskriptoren auch. Es vermeidet, den Namensraum mit einem weiteren Import zu überladen.
– Martijn Pieters
♦
10. Mai 2017 um 8:10 Uhr
Der vollständige Code für den vorgeschlagenen Deskriptoransatz lautet a.barFighters = barFighters.__get__(a)
– eqzx
14. September 2017 um 17:45 Uhr
Nur eine Anmerkung, es gibt keine ungebundene Methode in python3, da der Unterschied zwischen einer Funktion und einer ungebundenen Methode ziemlich minimal ist, beseitigt Python 3 die Unterscheidung.
– öter
1. Februar 2021 um 9:21 Uhr
Russland muss Putin entfernen
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 weglassen object
aber ansonsten sollte der Code gleich bleiben.
Hinzufügen einer Methode zu einer vorhandenen Objektinstanz
Ich habe gelesen, dass es möglich ist, eine Methode zu einem bestehenden Objekt (z. B. nicht in der Klassendefinition) in Python hinzuzufügen.
Ich verstehe, dass es nicht immer eine gute Entscheidung ist, dies zu tun. Aber wie könnte man das tun?
Ich empfehle das nicht. Das ist eine schlechte Idee. Tu es nicht.
Hier sind ein paar Gründe:
Daher schlage ich vor, dass Sie dies nicht tun, es sei denn, Sie haben einen wirklich guten Grund. Es ist viel besser, die richtige Methode in der Klassendefinition zu definieren oder weniger vorzugsweise um die Klasse direkt zu patchen, wie folgt:
Foo.sample_method = sample_method
Da es jedoch lehrreich ist, werde ich Ihnen einige Möglichkeiten zeigen, dies zu tun.
Hier ist ein Setup-Code. Wir brauchen eine Klassendefinition. Es könnte importiert werden, aber es spielt wirklich keine Rolle.
class Foo(object):
'''An empty class to demonstrate adding a method to an instance'''
Erstellen Sie eine Instanz:
foo = Foo()
Erstellen Sie eine Methode, um sie hinzuzufügen:
def sample_method(self, bar, baz):
print(bar + baz)
__get__
Gepunktete Suchen nach Funktionen rufen die auf __get__
Methode der Funktion mit der Instanz, bindet das Objekt an die Methode und erzeugt so eine “gebundene Methode”.
foo.sample_method = sample_method.__get__(foo)
und nun:
>>> foo.sample_method(1,2)
3
Importieren Sie zuerst Typen, von denen wir den Methodenkonstruktor erhalten:
import types
Jetzt fügen wir die Methode der Instanz hinzu. Dazu benötigen wir den MethodType-Konstruktor aus der types
Modul (das wir oben importiert haben).
Die Argumentsignatur für types.MethodType (in Python 3) ist (function, instance)
:
foo.sample_method = types.MethodType(sample_method, foo)
und Verwendung:
>>> foo.sample_method(1,2)
3
Übrigens war in Python 2 die Signatur (function, instance, class)
:
foo.sample_method = types.MethodType(sample_method, foo, Foo)
Zuerst erstellen wir eine Wrapper-Funktion, die die Methode an die Instanz bindet:
def bind(instance, method):
def binding_scope_fn(*args, **kwargs):
return method(instance, *args, **kwargs)
return binding_scope_fn
Verwendung:
>>> foo.sample_method = bind(foo, sample_method)
>>> foo.sample_method(1,2)
3
Eine partielle Funktion wendet das/die erste(n) Argument(e) auf eine Funktion (und optional Schlüsselwortargumente) an und kann später mit den verbleibenden Argumenten (und überschreibenden Schlüsselwortargumenten) aufgerufen werden. Daher:
>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3
Dies ist sinnvoll, wenn man bedenkt, dass gebundene Methoden Teilfunktionen der Instanz sind.
Wenn wir versuchen, sample_method auf die gleiche Weise hinzuzufügen, wie wir es der Klasse hinzufügen könnten, ist es von der Instanz ungebunden und nimmt das implizite Selbst nicht als erstes Argument.
>>> foo.sample_method = sample_method
>>> foo.sample_method(1,2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: sample_method() takes exactly 3 arguments (2 given)
Wir können die ungebundene Funktion zum Laufen bringen, indem wir die Instanz explizit übergeben (oder irgendetwas anderes, da diese Methode die nicht wirklich verwendet self
Argumentvariable), aber es wäre nicht konsistent mit der erwarteten Signatur anderer Instanzen (wenn wir diese Instanz mit Affenpatchen versehen):
>>> foo.sample_method(foo, 1, 2)
3
Sie kennen jetzt mehrere Möglichkeiten könnte tun Sie dies, aber in aller Ernsthaftigkeit – tun Sie dies nicht.
Der Haftungsausschluss ist, was ich mich gefragt habe. Methodendefinitionen sind einfach Funktionen, die innerhalb der Klassendefinition verschachtelt sind.
– Kalt
18. Januar 2018 um 19:06 Uhr
@Atcold Ich habe Gründe erläutert, dies in der Einführung zu vermeiden.
– Russland muss Putin entfernen
♦
18. Januar 2018 um 19:22 Uhr
@AidasBendoraitis Ich würde nicht sagen, dass es “braucht”, es ist ein optionaler Parameter, der beim Anwenden des Deskriptorprotokolls angegeben wird – aber Python-Funktionen verwenden das Argument nicht: github.com/python/cpython/blob/master/Objects/funcobject.c#L581
– Russland muss Putin entfernen
♦
31. Januar 2018 um 13:50 Uhr
Schöne Erklärung und Vorgehensweise.
– Karl Boneri
27. Januar 2021 um 23:06 Uhr
@AaronHall: Ich denke, das kann beim Spotten tatsächlich eine nützliche Technik sein. Angenommen, Sie möchten Ihr Mock-Objekt mit einer definierten Implementierung mit einem Monkeypatch versehen. Das ist leicht zu denken mock.sample_method = SampleClass.sample_method
wird einfach funktionieren…
– z33k
23. November 2021 um 16:11 Uhr
Modul neu ist seit Python 2.6 veraltet und wurde in 3.0 entfernt, use Typen
sehen http://docs.python.org/library/new.html
Im folgenden Beispiel habe ich absichtlich den Rückgabewert entfernt patch_me()
Funktion. Ich denke, dass die Angabe eines Rückgabewerts einen glauben machen könnte, dass Patch ein neues Objekt zurückgibt, was nicht wahr ist – es modifiziert das eingehende Objekt. Vermutlich kann dies einen disziplinierteren Umgang mit Monkeypatching erleichtern.
import types
class A(object):#but seems to work for old style objects too
pass
def patch_me(target):
def method(target,x):
print "x=",x
print "called from", target
target.method = types.MethodType(method,target)
#add more if needed
a = A()
print a
#out: <__main__.A object at 0x2b73ac88bfd0>
patch_me(a) #patch instance
a.method(5)
#out: x= 5
#out: called from <__main__.A object at 0x2b73ac88bfd0>
patch_me(A)
A.method(6) #can patch class too
#out: x= 6
#out: called from <class '__main__.A'>
Wie würde dies funktionieren, wenn die hinzugefügte Methode auf sich selbst verweisen muss? Mein erster Versuch führt zu einem Syntaxfehler, aber das Hinzufügen von self in der Definition der Methode scheint nicht zu funktionieren. Importtypen Klasse A(Objekt):#scheint aber auch für Objekte im alten Stil zu funktionieren ax = ‘ax’ pass def patch_me(target): def method(target,x): print (self.ax) print (“x=” ,x) print (“aufgerufen von”, Ziel) target.method = types.MethodType(method,target) #füge bei Bedarf weitere hinzu a = A() print(a.ax)
– WesR
31. Januar 2020 um 6:36 Uhr
Tomasz Zielinski
Ich denke, dass die obigen Antworten den entscheidenden Punkt verfehlt haben.
Lassen Sie uns eine Klasse mit einer Methode haben:
class A(object):
def m(self):
pass
Jetzt spielen wir damit in Ipython:
In [2]: A.m
Out[2]: <unbound method A.m>
OK, also M() irgendwie wird eine ungebundene Methode von A. Aber ist es wirklich so?
In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>
Es stellt sich heraus, dass M() ist nur eine Funktion, zu der eine Referenz hinzugefügt wird A Klassenwörterbuch – es gibt keine Magie. Warum dann Bin gibt uns eine ungebundene Methode? Das liegt daran, dass der Punkt nicht in eine einfache Wörterbuchsuche übersetzt wird. Es ist de facto ein Aufruf von A.__class__.__getattribute__(A, ‘m’):
In [11]: class MetaA(type):
....: def __getattribute__(self, attr_name):
....: print str(self), '-', attr_name
In [12]: class A(object):
....: __metaclass__ = MetaA
In [23]: A.m
<class '__main__.A'> - m
<class '__main__.A'> - m
Jetzt bin ich mir aus dem Kopf nicht sicher, warum die letzte Zeile zweimal gedruckt wird, aber es ist immer noch klar, was dort los ist.
Nun, was der Standardwert __getattribute__ macht, ist, dass er prüft, ob das Attribut ein sogenanntes ist Beschreibung oder nicht, dh ob es eine spezielle __get__-Methode implementiert. Wenn es diese Methode implementiert, ist das, was zurückgegeben wird, das Ergebnis des Aufrufs dieser __get__-Methode. Zurück zur ersten Version unserer A Klasse, das haben wir:
In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>
Und weil Python-Funktionen das Deskriptorprotokoll implementieren, binden sie sich, wenn sie im Namen eines Objekts aufgerufen werden, in ihrer __get__-Methode an dieses Objekt.
Ok, wie fügt man einem bestehenden Objekt eine Methode hinzu? Angenommen, es macht Ihnen nichts aus, die Klasse zu patchen, ist es so einfach wie:
B.m = m
Dann Bm “wird” dank der Deskriptormagie zu einer ungebundenen Methode.
Und wenn Sie nur einem einzelnen Objekt eine Methode hinzufügen möchten, müssen Sie die Maschinerie selbst emulieren, indem Sie types.MethodType verwenden:
b.m = types.MethodType(m, b)
Übrigens:
In [2]: A.m
Out[2]: <unbound method A.m>
In [59]: type(A.m)
Out[59]: <type 'instancemethod'>
In [60]: type(b.m)
Out[60]: <type 'instancemethod'>
In [61]: types.MethodType
Out[61]: <type 'instancemethod'>
John Downey
In Python funktioniert Monkeypatching im Allgemeinen, indem die Signatur einer Klasse oder Funktion mit Ihrer eigenen überschrieben wird. Nachfolgend ein Beispiel aus der Zope-Wiki:
from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak
Dieser Code überschreibt/erstellt eine aufgerufene Methode speak
in der Klasse. Bei Jeff Atwood Letzter Beitrag zum Patchen von Affenzeigte er ein Beispiel in C# 3.0, der aktuellen Sprache, die ich für die Arbeit verwende.
Aber es beeinflusst alle Instanzen der Klasse, nicht nur eine.
– glglgl
3. Juni 2014 um 10:06 Uhr
Danke, ich bin hierher gekommen, um nach einer Möglichkeit zu suchen, alle Instanzen zu beeinflussen.
– Johann Henckel
28. April 2022 um 21:15 Uhr
Sanjasch
Sie können Lambda verwenden, um eine Methode an eine Instanz zu binden:
def run(self):
print self._instanceString
class A(object):
def __init__(self):
self._instanceString = "This is instance string"
a = A()
a.run = lambda: run(a)
a.run()
Ausgang:
This is instance string
Aber es beeinflusst alle Instanzen der Klasse, nicht nur eine.
– glglgl
3. Juni 2014 um 10:06 Uhr
Danke, ich bin hierher gekommen, um nach einer Möglichkeit zu suchen, alle Instanzen zu beeinflussen.
– Johann Henckel
28. April 2022 um 21:15 Uhr
HS.
Was Sie suchen, ist setattr
Ich glaube. Verwenden Sie dies, um ein Attribut für ein Objekt festzulegen.
>>> def printme(s): print repr(s)
>>> class A: pass
>>> setattr(A,'printme',printme)
>>> a = A()
>>> a.printme() # s becomes the implicit 'self' variable
< __ main __ . A instance at 0xABCDEFG>
Dies patcht die Klasse A
nicht die Instanz a
.
– Ethan Furmann
24. September 2011 um 20:04 Uhr
Gibt es einen Grund zu verwenden setattr(A,'printme',printme)
statt einfach A.printme = printme
?
– Tobias Kienzler
9. August 2013 um 10:04 Uhr
Sinnvoll ist es, wenn man Methodennamen zur Laufzeit aufbaut.
– rr-
2. April 2016 um 7:35 Uhr