Réponses:
En Python, il existe une différence entre les fonctions et les méthodes liées.
>>> 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>>
>>>
Les méthodes liées ont été "liées" (comment descriptives) à une instance, et cette instance sera passée comme premier argument chaque fois que la méthode est appelée.
Les callables qui sont des attributs d'une classe (par opposition à une instance) ne sont toujours pas liés, cependant, vous pouvez donc modifier la définition de la classe quand vous le souhaitez:
>>> 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
Les instances précédemment définies sont également mises à jour (tant qu'elles n'ont pas remplacé l'attribut elles-mêmes):
>>> a.fooFighters()
fooFighters
Le problème survient lorsque vous souhaitez attacher une méthode à une seule instance:
>>> 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)
La fonction n'est pas automatiquement liée lorsqu'elle est attachée directement à une instance:
>>> a.barFighters
<function barFighters at 0x00A98EF0>
Pour le lier, nous pouvons utiliser la fonction MethodType dans le module types :
>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters
Cette fois, d'autres instances de la classe n'ont pas été affectées:
>>> a2.barFighters()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: A instance has no attribute 'barFighters'
Plus d'informations peuvent être trouvées en lisant sur les descripteurs et la programmation des métaclasses .
descriptor protocol
vs créer un MethodType
côté peut-être un peu plus lisible.
classmethod
et staticmethod
et d'autres descripteurs aussi. Il évite d'encombrer l'espace de noms avec encore une autre importation.
a.barFighters = barFighters.__get__(a)
Le module nouveau est déconseillé depuis python 2.6 et supprimé dans 3.0, utilisez les types
voir http://docs.python.org/library/new.html
Dans l'exemple ci-dessous, j'ai délibérément supprimé la valeur de retour de la patch_me()
fonction. Je pense que donner une valeur de retour peut faire croire que le patch renvoie un nouvel objet, ce qui n'est pas vrai - il modifie l'objet entrant. Cela peut probablement faciliter une utilisation plus disciplinée du monkeypatching.
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'>
Préface - une note sur la compatibilité: les autres réponses peuvent ne fonctionner qu'en Python 2 - cette réponse devrait parfaitement fonctionner en Python 2 et 3. Si vous écrivez Python 3 uniquement, vous pourriez laisser de côté l'héritage explicite object
, mais sinon le code devrait rester le même .
Ajout d'une méthode à une instance d'objet existante
J'ai lu qu'il est possible d'ajouter une méthode à un objet existant (par exemple pas dans la définition de classe) en Python.
Je comprends que ce n'est pas toujours une bonne décision de le faire. Mais comment pourrait-on faire cela?
Je ne le recommande pas. C'est une mauvaise idée. Ne le fais pas.
Voici quelques raisons:
Ainsi, je suggère que vous ne fassiez cela que si vous avez une très bonne raison. Il est préférable de définir la bonne méthode dans la définition de classe ou moins de préférence singe-patch la classe directement, comme ceci:
Foo.sample_method = sample_method
Puisqu'il est instructif, je vais vous montrer quelques façons de procéder.
Voici un code d'installation. Nous avons besoin d'une définition de classe. Il pourrait être importé, mais cela n'a pas vraiment d'importance.
class Foo(object):
'''An empty class to demonstrate adding a method to an instance'''
Créez une instance:
foo = Foo()
Créez une méthode pour y ajouter:
def sample_method(self, bar, baz):
print(bar + baz)
__get__
Les recherches en pointillé sur les fonctions appellent la __get__
méthode de la fonction avec l'instance, liant l'objet à la méthode et créant ainsi une "méthode liée".
foo.sample_method = sample_method.__get__(foo)
et maintenant:
>>> foo.sample_method(1,2)
3
Tout d'abord, importez les types, à partir desquels nous obtiendrons le constructeur de la méthode:
import types
Maintenant, nous ajoutons la méthode à l'instance. Pour ce faire, nous avons besoin du constructeur MethodType du types
module (que nous avons importé ci-dessus).
La signature d'argument pour types.MethodType est (function, instance, class)
:
foo.sample_method = types.MethodType(sample_method, foo, Foo)
et utilisation:
>>> foo.sample_method(1,2)
3
Tout d'abord, nous créons une fonction wrapper qui lie la méthode à l'instance:
def bind(instance, method):
def binding_scope_fn(*args, **kwargs):
return method(instance, *args, **kwargs)
return binding_scope_fn
usage:
>>> foo.sample_method = bind(foo, sample_method)
>>> foo.sample_method(1,2)
3
Une fonction partielle applique le (s) premier (s) argument (s) à une fonction (et éventuellement des arguments de mot-clé) et peut ensuite être appelée avec les arguments restants (et les arguments de mot-clé prioritaires). Donc:
>>> from functools import partial
>>> foo.sample_method = partial(sample_method, foo)
>>> foo.sample_method(1,2)
3
Cela est logique lorsque vous considérez que les méthodes liées sont des fonctions partielles de l'instance.
Si nous essayons d'ajouter le sample_method de la même manière que nous pourrions l'ajouter à la classe, il n'est pas lié à l'instance et ne prend pas le self implicite comme premier 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)
Nous pouvons faire fonctionner la fonction non liée en passant explicitement l'instance (ou quoi que ce soit, car cette méthode n'utilise pas réellement la self
variable d'argument), mais elle ne serait pas cohérente avec la signature attendue des autres instances (si nous corrigeons des singes cette instance):
>>> foo.sample_method(foo, 1, 2)
3
Vous savez maintenant plusieurs façons dont vous pouvez le faire, mais dans tout le sérieux - ne le faites pas.
__get__
méthode doit également la classe comme paramètre suivant: sample_method.__get__(foo, Foo)
.
Je pense que les réponses ci-dessus ont manqué le point clé.
Ayons une classe avec une méthode:
class A(object):
def m(self):
pass
Maintenant, jouons avec lui en ipython:
In [2]: A.m
Out[2]: <unbound method A.m>
Ok, alors m () devient en quelque sorte une méthode non liée de A . Mais est-ce vraiment comme ça?
In [5]: A.__dict__['m']
Out[5]: <function m at 0xa66b8b4>
Il s'avère que m () n'est qu'une fonction à laquelle une référence est ajoutée dans le dictionnaire de classe A - il n'y a pas de magie. Alors pourquoi Am nous donne une méthode non liée? C'est parce que le point n'est pas traduit en une simple recherche de dictionnaire. C'est de facto un appel de la classe A .__ __.__ 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
Maintenant, je ne sais pas du haut de ma tête pourquoi la dernière ligne est imprimée deux fois, mais il est toujours clair ce qui se passe là-bas.
Maintenant, ce que fait __getattribute__ par défaut, c'est qu'il vérifie si l'attribut est un soi-disant descripteur ou non, c'est-à-dire s'il implémente une méthode spéciale __get__. S'il implémente cette méthode, ce qui est renvoyé est le résultat de l'appel de cette méthode __get__. Pour en revenir à la première version de notre classe A, voici ce que nous avons:
In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>
Et parce que les fonctions Python implémentent le protocole de descripteur, si elles sont appelées au nom d'un objet, elles se lient à cet objet dans leur méthode __get__.
Ok, alors comment ajouter une méthode à un objet existant? En supposant que cela ne vous dérange pas de corriger la classe, c'est aussi simple que:
B.m = m
Alors Bm "devient" une méthode non liée, grâce au descripteur magique.
Et si vous souhaitez ajouter une méthode uniquement à un seul objet, vous devez émuler la machinerie vous-même, à l'aide de types.MethodType:
b.m = types.MethodType(m, b)
Au fait:
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'>
En Python, le patch de singe fonctionne généralement en remplaçant une signature de classe ou de fonction par la vôtre. Voici un exemple du Zope Wiki :
from SomeOtherProduct.SomeModule import SomeClass
def speak(self):
return "ook ook eee eee eee!"
SomeClass.speak = speak
Ce code écrasera / créera une méthode appelée speak sur la classe. Dans le récent post de Jeff Atwood sur le patch de singe . Il montre un exemple en C # 3.0 qui est le langage actuel que j'utilise pour le travail.
Vous pouvez utiliser lambda pour lier une méthode à une instance:
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()
Production:
This is instance string
Il existe au moins deux façons d'attacher une méthode à une instance sans 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>>
Liens utiles:
Modèle de données - appel de descripteurs Guide descriptif des
descripteurs - appel de descripteurs
Ce que vous cherchez, c'est setattr
je crois. Utilisez-le pour définir un attribut sur un objet.
>>> 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
, pas l'instance a
.
setattr(A,'printme',printme)
au lieu de simplement A.printme = printme
?
Étant donné que cette question demandait des versions non Python, voici JavaScript:
a.methodname = function () { console.log("Yay, a new method!") }
Consolidation des réponses de Jason Pratt et du wiki de la communauté, avec un aperçu des résultats des différentes méthodes de liaison:
Notez en particulier comment l'ajout de la fonction de liaison en tant que méthode de classe fonctionne , mais la portée de référence est incorrecte.
#!/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>>
Personnellement, je préfère la route de la fonction ADDMETHOD externe, car elle me permet également d'attribuer dynamiquement de nouveaux noms de méthode dans un itérateur.
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
réécrit de la manière suivante def addmethod(self, method, name): self.__dict__[name] = types.MethodType( method, self )
résout le problème
Bien que la réponse de Jasons fonctionne, cela ne fonctionne que si l'on veut ajouter une fonction à une classe. Cela n'a pas fonctionné pour moi lorsque j'ai essayé de recharger une méthode déjà existante à partir du fichier de code source .py.
Il m'a fallu des siècles pour trouver une solution de contournement, mais l'astuce semble simple ... 1.st importer le code à partir du fichier de code source 2.nd forcer un rechargement 3.rd types d'utilisation.FunctionType (...) pour convertir le méthode importée et liée à une fonction, vous pouvez également transmettre les variables globales actuelles, car la méthode rechargée serait dans un espace de noms différent 4.e maintenant, vous pouvez continuer comme suggéré par "Jason Pratt" en utilisant les types.MethodType (... )
Exemple:
# 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
Si cela peut être utile, j'ai récemment publié une bibliothèque Python nommée Gorilla pour rendre le processus de correction de singe plus pratique.
L'utilisation d'une fonction needle()
pour patcher un module nommé se guineapig
déroule comme suit:
import gorilla
import guineapig
@gorilla.patch(guineapig)
def needle():
print("awesome")
Mais il prend également en charge des cas d'utilisation plus intéressants, comme indiqué dans la FAQ de la documentation .
Le code est disponible sur GitHub .
Cette question a été ouverte il y a des années, mais bon, il existe un moyen simple de simuler la liaison d'une fonction à une instance de classe à l'aide de décorateurs:
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 ()
Là, lorsque vous transmettez la fonction et l'instance au décorateur de classeur, il crée une nouvelle fonction, avec le même objet de code que le premier. Ensuite, l'instance donnée de la classe est stockée dans un attribut de la fonction nouvellement créée. Le décorateur renvoie une (troisième) fonction appelant automatiquement la fonction copiée, donnant l'instance comme premier paramètre.
En conclusion, vous obtenez une fonction simulant sa liaison à l'instance de classe. Laisser la fonction d'origine inchangée.
Ce que Jason Pratt a posté est correct.
>>> 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'>
Comme vous pouvez le voir, Python ne considère pas b () différent de a (). En Python, toutes les méthodes ne sont que des variables qui se trouvent être des fonctions.
Test
, pas une instance de celle-ci.
Je trouve étrange que personne n'ait mentionné que toutes les méthodes répertoriées ci-dessus créent une référence de cycle entre la méthode ajoutée et l'instance, provoquant la persistance de l'objet jusqu'à la récupération de place. Il y avait une vieille astuce pour ajouter un descripteur en étendant la classe de l'objet:
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)) )
Avec cela, vous pouvez utiliser le pointeur automatique
MethodType
, appelez manuellement le protocole de descripteur et demandez à la fonction de produire votre instance:barFighters.__get__(a)
produit une méthodebarFighters
liée pour lié àa
.