772 votes

Ajout d’une méthode à un objet existant

J'ai lu qu'il est possible d'ajouter une méthode à un objet existant (par exemple, pas dans la définition de la classe) en python, je pense que cela s'appelle Monkey Patching (ou, dans certains cas, Canard coup de poing). Je comprends que ce n'est pas toujours une bonne décision de le faire. Mais, comment peut-on faire cela?

Et si vous ne savez pas python, pouvez la langue de votre choix faire? Si oui, comment?

MISE À JOUR 8/04/2008 00:21:01 HNE:

Qui ressemble à une bonne réponse de John Downey, j'ai essayé, mais il semble qu'il finit par n'être pas une véritable méthode. Votre exemple définit le nouveau patch de fonction avec un argument de soi, mais si vous écrivez le code de cette façon, le désormais patché méthode de classe demande un argument nommé soi (il n'est pas automatiquement reconnaître que l'identité de classe, qui est ce qui se passerait si elle est définie à l'intérieur de la définition de la classe), ce qui signifie que vous devez appeler la classe.patch(classe) au lieu de simplement la classe.patch() si vous voulez la même fonctionnalité qu'une véritable méthode. Il ressemble python n'est pas vraiment de la traiter comme une méthode, mais seulement comme une variable qui se trouve être une fonction (et en tant que tel est exigible). Est-il possible de joindre une méthode à une classe?

Oh, et Ryan, qui n'est pas exactement ce que je cherchais (il n'est pas intégré la fonctionnalité), mais c'est plutôt cool tout de même.

1074voto

Jason Pratt Points 4782

En Python, il y a 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é" (comment descriptive) à une instance, qui sera passé comme premier argument à chaque fois que la méthode est appelée.

Callables qui sont des attributs d'une classe (par opposition à une instance) sont encore indépendant, cependant, de sorte que vous pouvez modifier la définition de la classe à chaque fois que vous voulez:

>>> 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

Précédemment défini les instances sont mises à jour (tant qu'ils n'ont pas remplacé l'attribut eux-mêmes):

>>> a.fooFighters()
fooFighters

Le problème vient quand vous voulez joindre 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é quand il est connecté directement à une instance:

>>> a.barFighters
<function barFighters at 0x00A98EF0>

Pour lier cela, nous pouvons utiliser la MethodType fonction des types de module:

>>> import types
>>> a.barFighters = types.MethodType( barFighters, a )
>>> a.barFighters
<bound method ?.barFighters of <__main__.A instance at 0x00A9BC88>>
>>> a.barFighters()
barFighters

Ce temps, les autres instances de la classe n'ont pas été touchés:

>>> 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 par la lecture sur les descripteurs et de la métaclasse de programmation.

104voto

Evgeny Points 5444

Module nouveau est déprécié depuis la version 2.6 de python et supprimé dans la version 3.0, utilisez les types de

voir http://docs.python.org/library/new.html

Dans l'exemple ci-dessous j'ai volontairement supprimé valeur de retour de patch_me()fonction. Je pense que donner la valeur de retour peut faire croire que le patch renvoie un nouvel objet, qui n'est pas vrai - il modifie l'arrivée. Sans doute cela peut faciliter une discipline d'utilisation de 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'>

42voto

Tomasz Zielinski Points 9300

Je pense que les réponses ci-dessus manqué le point clé.

Nous allons avoir une classe avec une méthode:

class A(object):
    def m(self):
        pass

Maintenant, nous allons jouer avec elle dans ipython:

In [2]: A.m
Out[2]: <unbound method A.m>

Ok, donc m() devient en quelque sorte un indépendant de la méthode de Une. 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 est ajouté à Une classe dictionary - il n'y a pas de magie. Alors pourquoi A. m nous donne un indépendant méthode? C'est parce que la dot n'est pas traduit par une simple recherche dans le dictionnaire. C'est de facto un appel de 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

Maintenant, je ne suis pas sûr de la partie supérieure de ma tête pourquoi la dernière ligne est imprimé deux fois, mais c'est clairement ce qui s y passe.

Maintenant, ce que la valeur par défaut __getattribute__ n'est qu'il vérifie si l'attribut est un descripteur ou pas, c'est à dire si elle met en œuvre un spécial __get__ méthode. Si elle met en œuvre cette méthode, puis ce qui est retourné est le résultat de l'appel qu' __get__ méthode. Revenir à la première version de sortir d'Une classe, c'est ce que nous avons:

In [28]: A.__dict__['m'].__get__(None, A)
Out[28]: <unbound method A.m>

Et parce que les fonctions Python de mettre en œuvre le descripteur de protocole, s'ils sont appelés sur le nom d'un objet, ils attachés à cet objet dans leur __get__ méthode.

Ok, donc comment faire pour ajouter une méthode à un objet existant? En supposant que vous n'avez pas l'esprit de rapiéçage de classe, c'est aussi simple que:

B.m = m

Puis B. m "devient" un indépendant méthode, grâce à la descripteur de la magie.

Et si vous souhaitez ajouter une méthode à un objet unique, alors vous avez à émuler les machines-même, en utilisant des types.MethodType:

b.m = types.MethodType(m, b)

Par la voie:

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'>

27voto

John Downey Points 6729

En Python monkey patching fonctionne généralement à l'écrasement d'une classe ou d'une des fonctions de signature avec votre propre. Ci-dessous est un exemple de la Zope Wiki:

from SomeOtherProduct.SomeModule import SomeClass

Ce code va remplacer/créer une méthode appelée parler à la classe. Dans Jeff Atwood du post récent sur monkey patching. Il montre un exemple en C# 3.0, ce qui est la langue que j'utilise pour le travail.

11voto

ndpu Points 8679

Il y a au moins deux façons de fixer une méthode d'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 d'invocation des descripteurs
Descripteur HowTo Guide d'invocation des descripteurs

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X