Existe-t-il un moyen en Python de remplacer une méthode de classe au niveau de l'instance? Par exemple:
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
boby.bark() # WoOoOoF!!
Réponses:
Veuillez ne pas faire cela comme indiqué. Votre code devient illisible lorsque vous identifiez une instance pour qu'elle soit différente de la classe.
Vous ne pouvez pas déboguer le code monkeypatched.
Lorsque vous trouvez un bogue dans bobyet print type(boby), vous verrez que (a) c'est un chien, mais (b) pour une raison obscure, il n'aboie pas correctement. C'est un cauchemar. Ne fais pas ça.
Veuillez faire ceci à la place.
class Dog:
def bark(self):
print "WOOF"
class BobyDog( Dog ):
def bark( self ):
print "WoOoOoF!!"
otherDog= Dog()
otherDog.bark() # WOOF
boby = BobyDog()
boby.bark() # WoOoOoF!!
Oui c'est possible:
class Dog:
def bark(self):
print "Woof"
def new_bark(self):
print "Woof Woof"
foo = Dog()
funcType = type(Dog.bark)
# "Woof"
foo.bark()
# replace bark with new_bark for this object only
foo.bark = funcType(new_bark, foo, Dog)
foo.bark()
# "Woof Woof"
funcTypefait et pourquoi cela est nécessaire.
funcType = types.MethodType(après l'importation types) au lieu de funcType = type(Dog.bark).
__get__la fonction pour la lier à l'instance.
Vous devez utiliser MethodType à partir du typesmodule. Le but de MethodTypeest d'écraser les méthodes au niveau de l'instance (afin qu'elles selfpuissent être disponibles dans les méthodes écrasées).
Voir l'exemple ci-dessous.
import types
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
def _bark(self):
print "WoOoOoF!!"
boby.bark = types.MethodType(_bark, boby)
boby.bark() # WoOoOoF!!
Pour expliquer l'excellente réponse de @ codelogic, je propose une approche plus explicite. C'est la même technique que l' .opérateur approfondit pour lier une méthode de classe lorsque vous y accédez en tant qu'attribut d'instance, sauf que votre méthode sera en fait une fonction définie en dehors d'une classe.
En travaillant avec le code de @ codelogic, la seule différence réside dans la manière dont la méthode est liée. J'utilise le fait que les fonctions et les méthodes ne sont pas des descripteurs de données en Python et j'appelle la __get__méthode. Notez en particulier que l'original et le remplacement ont des signatures identiques, ce qui signifie que vous pouvez écrire le remplacement en tant que méthode de classe complète, en accédant à tous les attributs d'instance via self.
Chien de classe:
def bark (self):
imprimer "Woof"
def new_bark (soi):
imprimer "Woof Woof"
foo = Chien ()
# "Woof"
foo.bark ()
# remplace bark par new_bark pour cet objet uniquement
foo.bark = new_bark .__ get __ (toto, chien)
foo.bark ()
# "Woof Woof"
En affectant la méthode liée à un attribut d'instance, vous avez créé une simulation presque complète du remplacement d'une méthode. Une fonctionnalité pratique qui manque est l'accès à la version sans argument de super, puisque vous n'êtes pas dans une définition de classe. Une autre chose est que l' __name__attribut de votre méthode liée ne prendra pas le nom de la fonction qu'il remplace, comme il le ferait dans la définition de classe, mais vous pouvez toujours le définir manuellement. La troisième différence est que votre méthode liée manuellement est une simple référence d'attribut qui se trouve être une fonction. L' .opérateur ne fait rien d'autre que chercher cette référence. Lors de l'appel d'une méthode régulière à partir d'une instance, le processus de liaison crée une nouvelle méthode liée à chaque fois.
Soit dit en passant, la seule raison pour laquelle cela fonctionne est que les attributs d'instance remplacent les descripteurs sans données . Les descripteurs de données ont des __set__méthodes, ce qui n'est pas le cas (heureusement pour vous). Les descripteurs de données de la classe ont en fait priorité sur tous les attributs d'instance. C'est pourquoi vous pouvez affecter à une propriété: leur __set__méthode est appelée lorsque vous essayez de faire une affectation. Personnellement, j'aime aller plus loin et cacher la valeur réelle de l'attribut sous-jacent dans l'instance __dict__, où il est inaccessible par des moyens normaux exactement parce que la propriété l'ombrage.
Vous devez également garder à l'esprit que cela est inutile pour les méthodes magiques (double trait de soulignement) . Les méthodes magiques peuvent bien sûr être écrasées de cette manière, mais les opérations qui les utilisent ne regardent que le type. Par exemple, vous pouvez définir __contains__quelque chose de spécial dans votre instance, mais l'appel x in instancene tiendrait pas compte de cela et utiliserait à la type(instance).__contains__(instance, x)place. Cela s'applique à toutes les méthodes magiques spécifiées dans le modèle de données Python .
class Dog:
def bark(self):
print "WOOF"
boby = Dog()
boby.bark() # WOOF
# METHOD OVERRIDE
def new_bark():
print "WoOoOoF!!"
boby.bark = new_bark
boby.bark() # WoOoOoF!!
Vous pouvez utiliser la bobyvariable à l'intérieur de la fonction si vous en avez besoin. Puisque vous remplacez la méthode uniquement pour cet objet d'instance, cette méthode est plus simple et a exactement le même effet que l'utilisation self.
patchinget c'est la bonne façon de le faire (par exemple boby = Dog()et boby.bark = new_bark). Il est incroyablement utile dans les tests unitaires des contrôles. Pour plus d'explications, voir tryolabs.com/blog/2013/07/05/run-time-method-patching-python (exemples) - non, je ne suis pas affilié au site ou à l'auteur lié.
Puisque personne ne mentionne functools.partialici:
from functools import partial
class Dog:
name = "aaa"
def bark(self):
print("WOOF")
boby = Dog()
boby.bark() # WOOF
def _bark(self):
print("WoOoOoF!!")
boby.bark = partial(_bark, boby)
boby.bark() # WoOoOoF!!
Étant donné que les fonctions sont des objets de première classe en Python, vous pouvez les transmettre lors de l'initialisation de votre objet de classe ou le remplacer à tout moment pour une instance de classe donnée:
class Dog:
def __init__(self, barkmethod=None):
self.bark=self.barkp
if barkmethod:
self.bark=barkmethod
def barkp(self):
print "woof"
d=Dog()
print "calling original bark"
d.bark()
def barknew():
print "wooOOOoof"
d1=Dog(barknew)
print "calling the new bark"
d1.bark()
def barknew1():
print "nowoof"
d1.bark=barknew1
print "calling another new"
d1.bark()
et les résultats sont
calling original bark
woof
calling the new bark
wooOOOoof
calling another new
nowoof
Bien que j'aie aimé l'idée d'héritage de S. Lott et que je sois d'accord avec le 'type (a)', mais comme les fonctions ont aussi des attributs accessibles, je pense que cela peut être géré de cette façon:
class Dog:
def __init__(self, barkmethod=None):
self.bark=self.barkp
if barkmethod:
self.bark=barkmethod
def barkp(self):
"""original bark"""
print "woof"
d=Dog()
print "calling original bark"
d.bark()
print "that was %s\n" % d.bark.__doc__
def barknew():
"""a new type of bark"""
print "wooOOOoof"
d1=Dog(barknew)
print "calling the new bark"
d1.bark()
print "that was %s\n" % d1.bark.__doc__
def barknew1():
"""another type of new bark"""
print "nowoof"
d1.bark=barknew1
print "another new"
d1.bark()
print "that was %s\n" % d1.bark.__doc__
et la sortie est:
calling original bark
woof
that was original bark
calling the new bark
wooOOOoof
that was a new type of bark
another new
nowoof
that was another type of new bark
Cher, cela ne remplace pas, vous appelez simplement la même fonction deux fois avec l'objet. Fondamentalement, le remplacement est lié à plusieurs classes. lorsque la même méthode de signature existe dans différentes classes, la fonction que vous appelez décide de l'objet qui l'appelle. Le remplacement est possible en python lorsque vous créez plusieurs classes qui écrivent les mêmes fonctions et une chose de plus à partager que la surcharge n'est pas autorisée en python
J'ai trouvé que c'était la réponse la plus précise à la question initiale
https://stackoverflow.com/a/10829381/7640677
import a
def _new_print_message(message):
print "NEW:", message
a.print_message = _new_print_message
import b
b.execute()