Ich habe gelesen, dass es in Python möglich ist, eine Methode zu einem bestehenden Objekt (d.h. nicht in der Klassendefinition) hinzuzufügen.
Ich verstehe, dass das nicht immer gut ist. Aber wie könnte man das machen?
Ich habe gelesen, dass es in Python möglich ist, eine Methode zu einem bestehenden Objekt (d.h. nicht in der Klassendefinition) hinzuzufügen.
Ich verstehe, dass das nicht immer gut ist. 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 als erstes Argument übergeben, wenn die Methode aufgerufen wird.
Callables, die Attribute einer Klasse (im Gegensatz zu einer Instanz) sind, sind jedoch weiterhin ungebunden, so dass 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 entsteht, wenn Sie eine Methode an eine einzelne Instanz anhängen wollen:
>>> 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 ist nicht automatisch gebunden, wenn sie direkt mit einer Instanz verbunden ist:
>>> a.barFighters
<function barFighters at 0x00A98EF0>
Um sie zu binden, können wir die 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
Dieses Mal 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 Deskriptoren y Metaklasse Programmierung .
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 für Python 3 schreiben, können Sie die explizite Vererbung von object
aber ansonsten sollte der Code derselbe bleiben.
Hinzufügen einer Methode zu einer existierenden Objektinstanz
Ich habe gelesen, dass es in Python möglich ist, eine Methode zu einem bestehenden Objekt hinzuzufügen (z. B. nicht in der Klassendefinition).
Ich verstehe, dass dies nicht immer eine gute Entscheidung ist. Aber wie kann man das tun?
Ich empfehle das nicht. Das ist eine schlechte Idee. Tun Sie es nicht.
Dafür gibt es einige Gründe:
Ich schlage daher 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 o weniger vorzugsweise, um die Klasse direkt zu patchen, etwa so:
Foo.sample_method = sample_method
Da es aber lehrreich ist, werde ich Ihnen einige Möglichkeiten aufzeigen, wie Sie dies tun können.
Hier ist etwas Code für die Einrichtung. Wir brauchen eine Klassendefinition. Sie könnte importiert werden, aber das ist wirklich nicht wichtig.
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 zu ergänzen:
def sample_method(self, bar, baz):
print(bar + baz)
__get__
Gepunktete Lookups auf Funktionen rufen die __get__
Methode der Funktion mit der Instanz, wodurch das Objekt an die Methode gebunden wird und somit eine "gebundene Methode" entsteht.
foo.sample_method = sample_method.__get__(foo)
und jetzt:
>>> foo.sample_method(1,2)
3
Importieren Sie zunächst die Typen, von denen wir den Methodenkonstruktor erhalten werden:
import types
Jetzt fügen wir die Methode zur Instanz hinzu. Dazu benötigen wir den MethodType-Konstruktor aus der types
Modul (das wir oben importiert haben).
Die Argumentensignatur für types.MethodType (in Python 3) lautet (function, instance)
:
foo.sample_method = types.MethodType(sample_method, foo)
und Nutzung:
>>> foo.sample_method(1,2)
3
In Python 2 lautete die Signatur übrigens (function, instance, class)
:
foo.sample_method = types.MethodType(sample_method, foo, Foo)
Zunächst 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 Teilfunktion wendet das/die erste(n) Argument(e) auf eine Funktion (und optional auf Schlüsselwortargumente) an und kann später mit den übrigen Argumenten (und überschreibenden Schlüsselwortargumenten) aufgerufen werden. Also
>>> 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, die sample_method auf die gleiche Weise wie die Klasse hinzuzufügen, ist sie nicht an die Instanz gebunden und nimmt nicht das implizite self als erstes Argument an.
>>> 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 explizit die Instanz übergeben (oder irgendetwas anderes, da diese Methode nicht wirklich die self
Argumentvariable), aber es wäre nicht konsistent mit der erwarteten Signatur anderer Instanzen (wenn wir monkey-Parcheando diese Instanz sind):
>>> foo.sample_method(foo, 1, 2)
3
Sie kennen jetzt mehrere Möglichkeiten, wie Sie könnte tun, aber ganz im Ernst - tun Sie das nicht.
Modul neu ist seit Python 2.6 veraltet und wurde in 3.0 entfernt, verwenden Sie Typen
siehe http://docs.python.org/library/new.html
In dem folgenden Beispiel habe ich absichtlich den Rückgabewert von patch_me()
Funktion. Ich denke, dass die Angabe des Rückgabewerts den Eindruck erwecken kann, dass patch ein neues Objekt zurückgibt, was nicht stimmt - es verändert das eingehende Objekt. Wahrscheinlich kann dies eine diszipliniertere Verwendung von 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'>
Ich denke, dass die obigen Antworten den Kernpunkt verfehlt haben.
Nehmen wir eine Klasse mit einer Methode:
class A(object):
def m(self):
pass
Spielen wir nun damit in ipython:
In [2]: A.m
Out[2]: <unbound method A.m>
Ok, also m() wird irgendwie zu einer ungebundenen Methode von A . Aber ist das wirklich so?
In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>
Es stellt sich heraus, dass m() ist nur eine Funktion, deren Verweis in A Klassenwörterbuch - es gibt keine Magie. Warum dann A.m gibt uns eine ungebundene Methode? Das liegt daran, dass der Punkt nicht in eine einfache Wörterbuchabfrage ü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
Ich weiß zwar nicht genau, warum die letzte Zeile zweimal gedruckt wird, aber es ist doch klar, was hier vor sich geht.
Die Standardfunktion __getattribute__ prüft, ob es sich bei dem Attribut um ein sogenanntes Deskriptor oder nicht, d.h. ob es eine spezielle __get__-Methode implementiert. Wenn sie diese Methode implementiert, dann ist das, was zurückgegeben wird, das Ergebnis des Aufrufs dieser __get__-Methode. Gehen wir zurück zur ersten Version unserer A Klasse, das ist es, was wir haben:
In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>
Und da Python-Funktionen das Deskriptor-Protokoll implementieren, binden sie sich, wenn sie im Namen eines Objekts aufgerufen werden, in ihrer __get__-Methode an dieses Objekt.
Ok, wie fügt man also eine Methode zu einem bestehenden Objekt hinzu? Angenommen, die Klasse Parcheando stört dich nicht, dann ist es so einfach wie:
B.m = m
Dann B.m "wird dank der Deskriptor-Magie zu einer ungebundenen Methode.
Und wenn Sie eine Methode nur zu einem einzelnen Objekt hinzufügen wollen, müssen Sie die Maschinerie selbst nachbilden, indem Sie types.MethodType verwenden:
b.m = types.MethodType(m, b)
Im Übrigen:
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'>
In Python funktioniert Monkeypatching im Allgemeinen durch das Überschreiben der Signatur einer Klasse oder Funktion mit Ihrer eigenen. 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/erzeugt eine Methode namens speak
in der Klasse. In Jeff Atwoods jüngster Beitrag auf monkey Parcheando Er zeigte ein Beispiel in C# 3.0, der Sprache, die ich derzeit bei der Arbeit verwende.
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.