2008-12-27 7 views

Répondre

22

S'il vous plaît Ne le faites pas comme indiqué. Votre code devient illisible lorsque vous monkeypatch une instance pour être différente de la classe.

Vous ne pouvez pas déboguer du code monokypatch.

Lorsque vous trouvez un bug 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 le faire à 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!! 
+6

@arivero: Je pensais que le "S'il vous plaît ne pas le faire comme le montre" fait parfaitement clair. Quels autres mots aimeriez-vous voir pour clarifier le fait que cela ne répond pas à la question posée, mais donne des conseils sur les raisons pour lesquelles c'est une mauvaise idée? –

+17

Je ne suis pas en désaccord dans le conseil, ni le OP comme il semble. Mais je suppose que les gens ont des raisons de demander. Ou même si le PO n'a pas, d'autres futurs visiteurs pourraient. Donc, à mon humble avis, une réponse plus une réprimande est préférable que juste une réprimande. – arivero

+7

@arivero: Cela n'a pas répondu à ma question. –

120

Oui, il 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" 
+1

Un petit commentaire, quand vous faites funcType (new_bark, foo, Dog) son ajouter le nom == aboyer et la méthode d'instance Dog.new_bark dans foo .__ dict__ right? Donc, quand vous appelez à nouveau, cherchez d'abord dans le dictionnaire d'instance et appelez pour cela, –

+5

Veuillez expliquer ce que cela fait, en particulier ce que 'funcType' fait et pourquoi c'est nécessaire. –

+1

Je pense que cela peut être rendu un peu plus simple et plus explicite en utilisant 'funcType = types.MethodType' (après avoir importé' types') au lieu de 'funcType = type (Dog.bark)'. –

22
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 variable boby dans la fonction si vous avez besoin. Puisque vous remplacez la méthode juste pour cet objet instance, cette méthode est plus simple et a exactement le même effet que l'utilisation de self.

+1

À mon humble avis en utilisant la signature originale ajoute à la lisibilité, surtout si la fonction est définie ailleurs dans le code, pas près de l'instance. L'exception serait le cas où la méthode prioritaire est également utilisée indépendamment en tant que fonction. Bien sûr, dans cet exemple simple, cela n'a pas d'importance. – codelogic

+1

Je ne comprends pas pourquoi ce n'est pas la réponse acceptée. Cela s'appelle «patcher» et c'est la bonne façon de le faire (par exemple 'boby = Dog()' et 'boby.bark = new_bark'). Il est incroyablement utile dans l'unité * testing * pour les contrôles. Pour plus d'explications, voir https://tryolabs.com/blog/2013/07/05/run-time-method-patching-python/ (exemples) - non je ne suis pas affilié avec le site ou l'auteur. – Geoff

+2

La méthode new_bark n'a pas accès à self (instance), donc l'utilisateur n'a aucun moyen d'accéder aux propriétés de l'instance dans new_bark. Au lieu de cela, il faut utiliser MethodType du module types (voir ma réponse ci-dessous). –

0

Puisque les fonctions sont des objets de première classe en Python vous pouvez les passer lors de l'initialisation de votre objet de classe ou passer outre à 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 
-4

Bien que je l'ai aimé l'idée de l'héritage de S. Lott et d'accord avec le « type (a) » chose, mais étant donné que les fonctions ont aussi des attributs accessibles, je pense que ce 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 
+2

Si cela doit être "géré", alors - pour moi - quelque chose ne va pas. Surtout quand il y a une fonctionnalité de langue de première classe qui fait déjà le travail. –

-4

Cher cela ne surchargez pas vous appelez simplement la même fonction deux fois avec l'objet. Fondamentalement, le dépassement est lié à plus d'une classe. Quand la même méthode de signature existe dans différentes classes, alors quelle fonction vous appelez ceci décidez l'objet qui l'appelle. Le surpassement est possible en python quand vous faites que plus d'une classe écrit les mêmes fonctions et une chose de plus pour partager que la surcharge n'est pas autorisée en python

12

Vous devez utiliser MethodType du module types. Le but de MethodType est de remplacer les méthodes de niveau d'instance (de sorte que self peut être disponible dans une méthode écrasée).

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

Pour expliquer l'excellent @ codelogic réponse, je vous 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 utilisant 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 données descriptors en Python, et en invoquant la méthode __get__. 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. En affectant la méthode liée à un attribut d'instance, vous avez créé une simulation presque complète de substitution d'une méthode. Une caractéristique 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'attribut __name__ de votre méthode liée ne prendra pas le nom de la fonction qu'elle surcharge, comme ce serait le cas 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 référence d'attribut simple qui se trouve être une fonction. L'opérateur . ne fait que récupérer cette référence. Lorsque vous invoquez une méthode régulière à partir d'une instance, le processus de liaison crée une nouvelle méthode liée à chaque fois. La seule raison pour laquelle cela fonctionne est que les attributs d'instance remplacent descripteurs non-data. Les descripteurs de données ont __set__ méthodes, quelles méthodes (heureusement pour vous) ne le font pas. Les descripteurs de données de la classe sont prioritaires sur les attributs d'instance. C'est pourquoi vous pouvez assigner à une propriété: leur méthode __set__ est invoquée lorsque vous essayez de faire une affectation. Personnellement, j'aime aller un peu plus loin et cacher la valeur réelle de l'attribut sous-jacent dans __dict__ de l'instance, où il est inaccessible par des moyens normaux exactement parce que la propriété l'ombre.

Vous devez également garder à l'esprit que cela est inutile pour magic (double underscore) methods. Les méthodes magiques peuvent bien sûr être remplacées de cette manière, mais les opérations qui les utilisent ne regardent que le type. Par exemple, vous pouvez définir __contains__ pour quelque chose de spécial dans votre instance, mais l'appel x in instance ne tiendra pas compte de cela et utilisera type(instance).__contains__(instance, x) à la place. Ceci s'applique à toutes les méthodes magiques spécifiées dans le Python data model.

Questions connexes