Antworten:
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 .
descriptor protocol
vs erstellen eine MethodType
Seite, vielleicht ein wenig besser lesbar zu sein.
classmethod
und staticmethod
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.
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'>
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 .
Hinzufügen einer Methode zu einer vorhandenen Objektinstanz
Ich habe gelesen, dass es möglich ist, einem vorhandenen Objekt (z. B. nicht in der Klassendefinition) in Python eine Methode hinzuzufügen.
Ich verstehe, dass es nicht immer eine gute Entscheidung ist, dies zu tun. Aber wie könnte man das machen?
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:
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 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".
foo.sample_method = sample_method.__get__(foo)
und nun:
>>> foo.sample_method(1,2)
3
Importieren Sie zunächst die 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 dem types
Modul (das wir oben importiert haben).
Die Argument-Signatur für types.MethodType lautet (function, instance, class)
:
foo.sample_method = types.MethodType(sample_method, foo, Foo)
und Verwendung:
>>> foo.sample_method(1,2)
3
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
Verwendungszweck:
>>> foo.sample_method = bind(foo, sample_method)
>>> foo.sample_method(1,2)
3
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:
>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3
Dies ist sinnvoll, wenn Sie bedenken, dass gebundene Methoden Teilfunktionen der Instanz sind.
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.
>>> 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 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):
>>> foo.sample_method(foo, 1, 2)
3
Sie kennen jetzt verschiedene Möglichkeiten, wie Sie dies tun können, aber im Ernst - tun Sie dies nicht.
__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:
class A(object):
def m(self):
pass
Lassen Sie uns jetzt in ipython damit spielen:
In [2]: A.m
Out[2]: <unbound method A.m>
Ok, so m () wird irgendwie ein ungebundenes Methode A . Aber ist es wirklich so?
In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>
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'):
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 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:
In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>
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:
B.m = m
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:
b.m = types.MethodType(m, b)
Apropos:
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 das Patchen von Affen im Allgemeinen durch Überschreiben einer Klassen- oder Funktionssignatur mit Ihrer eigenen. Unten ist ein Beispiel aus dem Zope-Wiki :
from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak
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.
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()
Ausgabe:
This is instance string
Es gibt mindestens zwei Möglichkeiten, eine Methode an eine Instanz anzuhängen, ohne types.MethodType
:
>>> class A:
... def m(self):
... print 'im m, invoked with: ', self
>>> a = A()
>>> a.m()
im m, invoked with: <__main__.A instance at 0x973ec6c>
>>> a.m
<bound method A.m of <__main__.A instance at 0x973ec6c>>
>>>
>>> def foo(firstargument):
... print 'im foo, invoked with: ', firstargument
>>> foo
<function foo at 0x978548c>
1:
>>> a.foo = foo.__get__(a, A) # or foo.__get__(a, type(a))
>>> a.foo()
im foo, invoked with: <__main__.A instance at 0x973ec6c>
>>> a.foo
<bound method A.foo of <__main__.A instance at 0x973ec6c>>
2:
>>> instancemethod = type(A.m)
>>> instancemethod
<type 'instancemethod'>
>>> a.foo2 = instancemethod(foo, a, type(a))
>>> a.foo2()
im foo, invoked with: <__main__.A instance at 0x973ec6c>
>>> a.foo2
<bound method instance.foo of <__main__.A instance at 0x973ec6c>>
Nützliche Links:
Datenmodell - Aufrufen von Deskriptoren
Descriptor HowTo Guide - Aufrufen von Deskriptoren
Was Sie suchen, setattr
glaube ich. Verwenden Sie diese Option, 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>
A
, nicht die Instanz a
.
setattr(A,'printme',printme)
statt einfach zu verwenden A.printme = printme
?
Da diese Frage für Nicht-Python-Versionen gestellt wurde, ist hier JavaScript:
a.methodname = function () { console.log("Yay, a new method!") }
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.
#!/usr/bin/python -u
import types
import inspect
## dynamically adding methods to a unique instance of a class
# get a list of a class's method type attributes
def listattr(c):
for m in [(n, v) for n, v in inspect.getmembers(c, inspect.ismethod) if isinstance(v,types.MethodType)]:
print m[0], m[1]
# externally bind a function as a method of an instance of a class
def ADDMETHOD(c, method, name):
c.__dict__[name] = types.MethodType(method, c)
class C():
r = 10 # class attribute variable to test bound scope
def __init__(self):
pass
#internally bind a function as a method of self's class -- note that this one has issues!
def addmethod(self, method, name):
self.__dict__[name] = types.MethodType( method, self.__class__ )
# predfined function to compare with
def f0(self, x):
print 'f0\tx = %d\tr = %d' % ( x, self.r)
a = C() # created before modified instnace
b = C() # modified instnace
def f1(self, x): # bind internally
print 'f1\tx = %d\tr = %d' % ( x, self.r )
def f2( self, x): # add to class instance's .__dict__ as method type
print 'f2\tx = %d\tr = %d' % ( x, self.r )
def f3( self, x): # assign to class as method type
print 'f3\tx = %d\tr = %d' % ( x, self.r )
def f4( self, x): # add to class instance's .__dict__ using a general function
print 'f4\tx = %d\tr = %d' % ( x, self.r )
b.addmethod(f1, 'f1')
b.__dict__['f2'] = types.MethodType( f2, b)
b.f3 = types.MethodType( f3, b)
ADDMETHOD(b, f4, 'f4')
b.f0(0) # OUT: f0 x = 0 r = 10
b.f1(1) # OUT: f1 x = 1 r = 10
b.f2(2) # OUT: f2 x = 2 r = 10
b.f3(3) # OUT: f3 x = 3 r = 10
b.f4(4) # OUT: f4 x = 4 r = 10
k = 2
print 'changing b.r from {0} to {1}'.format(b.r, k)
b.r = k
print 'new b.r = {0}'.format(b.r)
b.f0(0) # OUT: f0 x = 0 r = 2
b.f1(1) # OUT: f1 x = 1 r = 10 !!!!!!!!!
b.f2(2) # OUT: f2 x = 2 r = 2
b.f3(3) # OUT: f3 x = 3 r = 2
b.f4(4) # OUT: f4 x = 4 r = 2
c = C() # created after modifying instance
# let's have a look at each instance's method type attributes
print '\nattributes of a:'
listattr(a)
# OUT:
# attributes of a:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FD88>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FD88>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FD88>>
print '\nattributes of b:'
listattr(b)
# OUT:
# attributes of b:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x000000000230FE08>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x000000000230FE08>>
# f0 <bound method C.f0 of <__main__.C instance at 0x000000000230FE08>>
# f1 <bound method ?.f1 of <class __main__.C at 0x000000000237AB28>>
# f2 <bound method ?.f2 of <__main__.C instance at 0x000000000230FE08>>
# f3 <bound method ?.f3 of <__main__.C instance at 0x000000000230FE08>>
# f4 <bound method ?.f4 of <__main__.C instance at 0x000000000230FE08>>
print '\nattributes of c:'
listattr(c)
# OUT:
# attributes of c:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002313108>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002313108>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002313108>>
Persönlich bevorzuge ich die externe ADDMETHOD-Funktionsroute, da ich damit auch innerhalb eines Iterators dynamisch neue Methodennamen zuweisen kann.
def y(self, x):
pass
d = C()
for i in range(1,5):
ADDMETHOD(d, y, 'f%d' % i)
print '\nattributes of d:'
listattr(d)
# OUT:
# attributes of d:
# __init__ <bound method C.__init__ of <__main__.C instance at 0x0000000002303508>>
# addmethod <bound method C.addmethod of <__main__.C instance at 0x0000000002303508>>
# f0 <bound method C.f0 of <__main__.C instance at 0x0000000002303508>>
# f1 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f2 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f3 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
# f4 <bound method ?.y of <__main__.C instance at 0x0000000002303508>>
addmethod
neu geschrieben auf folgende Weise def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )
löst das Problem
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:
# this class resides inside ReloadCodeDemo.py
class A:
def bar( self ):
print "bar1"
def reloadCode(self, methodName):
''' use this function to reload any function of class A'''
import types
import ReloadCodeDemo as ReloadMod # import the code as module
reload (ReloadMod) # force a reload of the module
myM = getattr(ReloadMod.A,methodName) #get reloaded Method
myTempFunc = types.FunctionType(# convert the method to a simple function
myM.im_func.func_code, #the methods code
globals(), # globals to use
argdefs=myM.im_func.func_defaults # default values for variables if any
)
myNewM = types.MethodType(myTempFunc,self,self.__class__) #convert the function to a method
setattr(self,methodName,myNewM) # add the method to the function
if __name__ == '__main__':
a = A()
a.bar()
# now change your code and save the file
a.reloadCode('bar') # reloads the file
a.bar() # now executes the reloaded code
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 Namen guineapig
lautet wie folgt:
import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
print("awesome")
Es werden aber auch interessantere Anwendungsfälle behandelt, wie in den FAQ aus der Dokumentation gezeigt .
Der Code ist auf GitHub verfügbar .
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:
def binder (function, instance):
copy_of_function = type (function) (function.func_code, {})
copy_of_function.__bind_to__ = instance
def bound_function (*args, **kwargs):
return copy_of_function (copy_of_function.__bind_to__, *args, **kwargs)
return bound_function
class SupaClass (object):
def __init__ (self):
self.supaAttribute = 42
def new_method (self):
print self.supaAttribute
supaInstance = SupaClass ()
supaInstance.supMethod = binder (new_method, supaInstance)
otherInstance = SupaClass ()
otherInstance.supaAttribute = 72
otherInstance.supMethod = binder (new_method, otherInstance)
otherInstance.supMethod ()
supaInstance.supMethod ()
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.
Was Jason Pratt gepostet hat, ist richtig.
>>> class Test(object):
... def a(self):
... pass
...
>>> def b(self):
... pass
...
>>> Test.b = b
>>> type(b)
<type 'function'>
>>> type(Test.a)
<type 'instancemethod'>
>>> type(Test.b)
<type 'instancemethod'>
Wie Sie sehen können, betrachtet Python b () nicht anders als a (). In Python sind alle Methoden nur Variablen, die zufällig Funktionen sind.
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:
def addmethod(obj, name, func):
klass = obj.__class__
subclass = type(klass.__name__, (klass,), {})
setattr(subclass, name, func)
obj.__class__ = subclass
from types import MethodType
def method(self):
print 'hi!'
setattr( targetObj, method.__name__, MethodType(method, targetObj, type(method)) )
Hiermit können Sie den Selbstzeiger verwenden
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
.