2010-09-21 6 views
39

Tengo una clase, ubicada en un módulo separado, que no puedo cambiar.Monkey-patch Clase Python

from module import MyClass 

class ReplaceClass(object) 
    ... 

MyClass = ReplaceClass 

Esto no cambia MyClass en otro lugar que no sea este archivo. Sin embargo, si agrego un método como este

def bar(): 
    print 123 

MyClass.foo = bar 

, esto funcionará y el método foo estará disponible en todas partes.

¿Cómo reemplazo la clase por completo?

Respuesta

52
import module 
class ReplaceClass(object): 
    .... 
module.MyClass = ReplaceClass 
+3

+1 por ser más rápido. solo asegúrese de que esto se ejecute antes de cualquier importación que use 'módulo.MyClass' – aaronasterling

+0

Ser más rápido no hace más correcto, pero realmente no creo que esto necesite una explicación adicional de todos modos. La diferencia debería ser bastante obvia una vez que vea cómo hacerlo. –

+15

Asegúrate de ser el "primero" en importar la clase. ¡Las referencias ya hechas a la clase "vieja" no serán reemplazadas! –

27

evitar los (;-) manera horrible from ... import para obtener barenames cuando lo que necesita con mayor frecuencia son calificados nombres. Una vez hecho cosas de la manera correcta Pythonic:

import module 

class ReplaceClass(object): ... 

module.MyClass = ReplaceClass 

De esta manera, usted está monkeypatching la módulo objeto, que es lo que necesita y no funcionará si no se usa ese módulo para otros. Con el formulario from ..., simplemente no tiene el objeto del módulo (una forma de ver el flagrante defecto del uso de la mayoría de las personas de from ...) y por lo tanto, obviamente está peor ;-);

La única forma en la que recomiendo el uso de la declaración from es importar un módulo dentro de un paquete:

from some.package.here import amodule 

por lo que todavía estás consiguiendo el objeto del módulo y utilizar nombres cualificados para todos los nombres en ese módulo

+2

1 pero tenga '' module.MyClass =, a pesar de que reemplazará a la clase en el módulo, no sustituirá a la clase en cualquier otro módulo que ya lo han hecho 'MyClass' de importación de módulo. Tendrán sus propias copias separadas. Monkey-patching es malo mmkay; 'from module import' también es malo mmkay; ambos al mismo tiempo son una receta para cosas que van mal. – bobince

+3

@bobince, ¡absolutamente! Si _otras_ partes del código ** ** también usar mi detestado 'MyClass' de importación de módulo, encontrar y monerías con todos ellos es en algún lugar entre "un desastre" y (' gc.get_referrers' "imposible" puede _sometimes_ ayuda. .. pero no siempre_!-). Sí, ambas son malas prácticas, pero a veces monkeypatching PUEDE ser el menor de los males, mientras que la creación de "nombres desnudos artificiales" con la declaración 'from' puede * always * ser dispensado. –

+0

Esto fue lo que resolvió mi problema. – chernevik

1
import some_module_name 

class MyClass(object): 
    ... #copy/paste source class and update/add your logic 

some_module_name.MyClass = MyClass 

Su preferible no cambiar el nombre de la clase, mientras que la sustitución, porque de alguna manera alguien puede haberles referencia mediante getattr - que dará lugar a fallar, como a continuación

getattr(some_module_name, 'MyClass') -> lo que se producirá un error si tiene reemplazó MyClass por ReplaceClass!

+0

1) esto solo tendrá efecto en el módulo que realiza la importación: agregar 'como foo' no hace nada para ayudar. 2) tu argumento sobre getattr no es solo confuso (debido a tu uso incoherente de nombres) sino que también es incorrecto y confunde el nombre de la clase con la clase misma. si 'importo algún modulo; somemodule.someclass = someotherclass', a continuación, en un momento posterior, en otro módulo, 'getattr (somemodule, 'someclass')' 'retornará someotherclass' que es exactamente lo que quiere OP. 3) la programación de copiar y pegar es fea y propensa a errores. – aaronasterling

+0

@AaronMcSmooth, de acuerdo!, Ahora he editado la respuesta, pero como OP quiere reemplazar a toda la clase, así que escribí copiar/pegar y añadir lógica/actualización, porque no es posible heredar esa clase original como estamos lo reemplazará y si OP quiere actualizar la lógica solo por unas pocas líneas, ¡entonces el código de reposo necesita obtenerlo de la fuente original! – shahjapan

+0

ahora su solución es la misma que los dos que ya se han presentado con la mancha añadido de un (todavía!) Una declaración incorrecta sobre 'getattr' también, por qué no podemos heredar de una clase que estamos a punto de 'reemplazar ¿? 'clase A (objeto): pase; clase B (A): pase; A = B; a = A() '. – aaronasterling

9

No soy más que un huevo. . . . Quizás es obvio para los no-novatos, pero necesitaba la expresión from some.package.module import module.

Tuve que modificar un método de OverallHelpfulClass. Esto falló:

import some.package.module 

class SpeciallyHelpfulClass(some.package.module.GenerallyHelpfulClass): 
    def general_method(self):... 

some.package.module.GenerallyHelpfulClass = SpeciallyHelpfulClass 

Se ejecutó el código, pero no usó los comportamientos sobrecargados en SpeciallyHelpfulClass.

Esto funcionó:

from some.package import module 

class SpeciallyHelpfulClass(module.GenerallyHelpfulClass): 
    def general_method(self):... 

module.GenerallyHelpfulClass = SpeciallyHelpfulClass 

Mi hipótesis es que la from ... import modismo 'consigue el módulo', como escribió Alex, ya que será recogido por otros módulos del paquete. Especulando aún más, la referencia punteada más larga parece llevar el módulo al espacio de nombres con la importación por referencia punteada larga, pero no cambia el módulo utilizado por otros espacios de nombres. Por lo tanto, los cambios en el módulo de importación solo aparecerían en el espacio de nombre donde se crearon. Es como si hubiera dos copias del mismo módulo, cada una disponible bajo referencias ligeramente diferentes.

+3

+1 a esto porque me parece más útil hablar sobre la ampliación de la clase que desea parchar y luego reemplazarla, en lugar de solo extender Object y reemplazarlo. –