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!!
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 boby
et 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"
la source
funcType
fait et pourquoi cela est nécessaire.funcType = types.MethodType
(après l'importationtypes
) au lieu defuncType = type(Dog.bark)
.__get__
la fonction pour la lier à l'instance.Vous devez utiliser MethodType à partir du
types
module. Le but deMethodType
est d'écraser les méthodes au niveau de l'instance (afin qu'ellesself
puissent ê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!!
la source
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 viaself
.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'appelx in instance
ne tiendrait pas compte de cela et utiliserait à latype(instance).__contains__(instance, x)
place. Cela s'applique à toutes les méthodes magiques spécifiées dans le modèle de données Python .la source
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
boby
variable à 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'utilisationself
.la source
patching
et c'est la bonne façon de le faire (par exempleboby = Dog()
etboby.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.partial
ici: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!!
la source
É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
la source
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:
la source
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
la source
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()
la source