2008-12-27 8 views

Respuesta

22

favor no hagas esto como se muestra. Su código se vuelve ilegible cuando monopatchch una instancia para ser diferente de la clase.

No se puede depurar el código de monkeypatched.

Cuando encuentre un error en boby y print type(boby), verá que (a) es un perro, pero (b) por alguna razón oscura, no ladra correctamente. Esto es una pesadilla. No lo hagas.

Por favor, haga esto en su lugar.

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: Pensé que el "Por favor, no hagas esto como se muestra" lo dejó perfectamente claro. ¿Qué otra o diferentes palabras le gustaría ver para dejar más claro que esto no está respondiendo la pregunta que se le hizo, sino que le está dando consejos sobre por qué es una mala idea? –

+17

No estoy en desacuerdo en el asesoramiento, ni en el OP como parece. Pero supongo que la gente tiene razones para preguntar. O incluso si el OP no lo ha hecho, otros futuros visitantes podrían hacerlo. Entonces, en mi humilde opinión, una respuesta más una reprimenda es mejor que solo una reprimenda. – arivero

+7

@arivero: Eso no respondió mi pregunta. –

120

Sí, es posible:

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 pequeño comentario, cuando haces funcType (new_bark, foo, Dog) está agregando el nombre == ladrido y el método de instancia Dog.new_bark en foo .__ dict__ right? Entonces, cuando vuelva a llamar, primero busque en el diccionario de instancias y llame para eso, –

+5

Explique qué hace esto, especialmente qué hace 'funcType' y por qué es necesario. –

+1

Creo que esto puede hacerse un poco más simple y más explícito usando 'funcType = types.MethodType' (después de importar' types') en lugar 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!! 

Usted puede utilizar la variable boby dentro de la función si es necesario. Ya que está anulando el método solo para este objeto de una instancia, de esta manera es más simple y tiene exactamente el mismo efecto que usar self.

+1

En mi humilde opinión, el uso de la firma original agrega legibilidad, especialmente si la función está definida en otra parte del código, no cerca de la instancia. La excepción sería el caso donde el método de anulación también se utiliza de forma independiente como una función. Por supuesto, en este simple ejemplo, no importa. – codelogic

+1

No entiendo por qué esta no es la respuesta aceptada. Se llama 'parcheado' y esta es la forma correcta de hacerlo (por ejemplo, 'boby = Dog()' y 'boby.bark = new_bark'). Es increíblemente útil en la unidad * de prueba * para los controles. Para obtener más información, consulte https://tryolabs.com/blog/2013/07/05/run-time-method-patching-python/ (examples) - no, no estoy afiliado al sitio o autor vinculado. – Geoff

+2

método new_bark no tiene acceso a self (instancia) por lo que no hay forma de que el usuario pueda acceder a las propiedades de la instancia en new_bark. En su lugar, es necesario utilizar MethodType desde el módulo de tipos (ver mi respuesta a continuación). –

0

Dado que las funciones son objetos de primera clase en Python se pueden pasar al inicializar el objeto de clase o anular en cualquier momento para una instancia determinada clase:

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

y los resultados son

calling original bark 
woof 
calling the new bark 
wooOOOoof 
calling another new 
nowoof 
-4

Aunque me gustaba la idea de la herencia de S. Lott y de acuerdo con el 'tipo (a)' cosa, pero ya que las funciones también tienen atributos accesibles, creo que el se puede controlar de esta manera:

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__ 

y la salida es:

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 necesita ser "administrado", entonces - para mí - algo está mal. Especialmente cuando hay una función de idioma de primera clase que ya hace el trabajo. –

-4

Estimado esto no es reemplazar acaba está llamando a la misma función dos veces con el objeto. Básicamente, anular se relaciona con más de una clase. cuando existe el mismo método de firma en diferentes clases, entonces qué función está llamando, esto decide el objeto que llama a esto. La anulación es posible en Python cuando haces que más de una clase escriba las mismas funciones y una cosa más para compartir que la sobrecarga no está permitida en python

12

Tienes que usar MethodType del módulo de tipos. El propósito de MethodType es sobrescribir los métodos de nivel de instancia (para que self pueda estar disponible en el método sobrescrito).

vea el siguiente ejemplo.

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

Para explicar excelente respuesta de @ codelogic, propongo un enfoque más explícito.Esta es la misma técnica que el operador . realiza para vincular un método de clase cuando accede a él como un atributo de instancia, excepto que su método será en realidad una función definida fuera de una clase.

Al trabajar con el código @codelogic, la única diferencia está en cómo se enlaza el método. Estoy usando el hecho de que las funciones y los métodos no son datos descriptors en Python e invocan el método __get__. Tenga en cuenta especialmente que tanto el original como el reemplazo tienen firmas idénticas, lo que significa que puede escribir el reemplazo como un método de clase completa, accediendo a todos los atributos de instancia a través del self.

 
class Dog: 
    def bark(self): 
     print "Woof" 

def new_bark(self): 
    print "Woof Woof" 

foo = Dog() 

# "Woof" 
foo.bark() 

# replace bark with new_bark for this object only 
foo.bark = new_bark.__get__(foo, Dog) 

foo.bark() 
# "Woof Woof" 

Al asignar un método vinculado a un atributo de instancia, que han creado una simulación casi completa de sustituir un método. Una característica útil que falta es el acceso a la versión no-arg de super, ya que no se encuentra en una definición de clase. Otra cosa es que el atributo __name__ de su método encuadernado no tomará el nombre de la función que está anulando, como lo haría en la definición de clase, pero aún puede establecerlo manualmente. La tercera diferencia es que su método de enlace manual es una referencia de atributo simple que simplemente resulta ser una función. El operador . no hace más que buscar esa referencia. Por otro lado, cuando se invoca un método regular de una instancia, el proceso de enlace crea un nuevo método vinculado en todo momento.

La única razón por la que esto funciona, por cierto, es que los atributos de instancia anulan los descriptores sin datos. Los descriptores de datos tienen métodos __set__, que los métodos (afortunadamente para ti) no tienen. Los descriptores de datos en la clase realmente tienen prioridad sobre cualquier atributo de instancia. Es por eso que puede asignar una propiedad: se invoca su método __set__ cuando intenta realizar una tarea. Personalmente, me gusta llevar esto un paso más allá y ocultar el valor real del atributo subyacente en el caso __dict__, donde es inaccesible por medios normales exactamente porque la propiedad lo sombrea.

También debe tener en cuenta que esto no tiene sentido para magic (double underscore) methods. Por supuesto, los métodos mágicos pueden anularse de esta manera, pero las operaciones que los usan solo miran el tipo. Por ejemplo, puede configurar __contains__ como algo especial en su instancia, pero al llamar al x in instance no tendrá en cuenta eso y usará type(instance).__contains__(instance, x). Esto se aplica a todos los métodos mágicos especificados en Python data model.

Cuestiones relacionadas