2010-05-03 15 views
7

Necesito encontrar una manera elegante de hacer 2 tipos de MixIns.¿Cuáles son las maneras elegantes de hacer MixIns en Python?

Primero:

class A(object): 
    def method1(self): 
     do_something() 

Ahora, un MixInClass debe hacer method1 hacer esto: do_other() ->A.method1() ->do_smth_else() - es decir, básicamente, "envolver" la función más. Estoy bastante seguro de que debe existir una buena solución para esto.

Segundo:

class B(object): 
    def method1(self): 
     do_something() 
     do_more() 

En este caso, quiero MixInClass2 para poder inyectarse entre do_something() y do_more(), es decir .: do_something() ->MixIn.method1 ->do_more(). Entiendo que probablemente esto requiera modificar class B - eso está bien, solo estoy buscando las formas más simples de lograr esto.

Estos son problemas bastante triviales y en realidad los resolví, pero mi solución está contaminada.

Fisrt uno usando self._old_method1 = self.method1(); self.method1() = self._new_method1(); y escribiendo _new_method1() que llama a _old_method1().

Problema: múltiples MixIns cambiarán el nombre a _old_method1 y no es elegante.

Segundo MixIn uno se resolvió creando un método ficticio call_mixin(self): pass e inyectándolo entre llamadas y definiendo self.call_mixin(). De nuevo poco elegante y se romperá en múltiples MixIns ..

¿Alguna idea?


Gracias a Boldewyn, he encontrado la solución elegante al primero (he olvidado puede crear decoradores sobre la marcha, sin modificar código original):

class MixIn_for_1(object): 
    def __init__(self): 
     self.method1 = self.wrap1(self.method1) 
     super(MixIn_for_1, self).__init__() 

    def wrap1(self, old): 
     def method1(): 
      print "do_other()" 
      old() 
      print "do_smth_else()" 
     return method1 

Aún así la búsqueda para ideas para el segundo (esta idea no encajará, ya que necesito inyectar dentro del viejo método, no afuera, como en este caso).


Solución para segunda está por debajo, en sustitución de "pass_func" con lambda:0.

+0

Para su segunda pregunta, ¿necesita mezclar.method1 los parámetros y, de ser así, estos parámetros dependen de las variables/valores devueltos en do_something()? Y finalmente, ¿do_more() necesita el resultado de tu mixin? – KillianDS

+0

No, do_more() no necesita resultado. ¿Los argumentos? Probablemente solo necesite "yo", lo cual debería pasar de todos modos. –

Respuesta

3

Aquí hay otra manera de implementar MixInClass1, MixinClass2:

decoradores son útiles cuando se necesita para envolver muchas funciones. Desde MixinClass1 necesita para envolver una única función, creo que es más claro que el mono-patch:

El uso de dobles guiones para __old_method1 y __method1 juega un papel útil en MixInClass1.Debido a la convención de cambio de nombre de Python, el uso de los caracteres de subrayado dobles localiza estos atributos en MixinClass1 y le permite usar los mismos nombres de atributos para otras clases de mezcla sin causar colisiones de nombres no deseados.

class MixInClass1(object): 
    def __init__(self): 
     self.__old_method1,self.method1=self.method1,self.__method1 
     super(MixInClass1, self).__init__()   
    def __method1(self): 
     print "pre1()" 
     self.__old_method1() 
     print "post1()" 

class MixInClass2(object): 
    def __init__(self): 
     super(MixInClass2, self).__init__()   
    def method1_hook(self): 
     print('MixIn method1') 

class Foo(MixInClass2,MixInClass1): 
    def method1(self): 
     print "do_something()" 
     getattr(self,'method1_hook',lambda *args,**kw: None)() 
     print "do_more()" 

foo=Foo() 
foo.method1() 
+0

La primera solución es exactamente la misma que describí en cuestión. El segundo es inteligente, suponiendo que 'Foo' es' B' y pass_func es 'lambda: 0'. –

+0

@Slava: si usa 'pass_func', entonces no tendrá que cambiar' Foo' si luego decide pasar argumentos a 'method1_hook'. Si usa 'lambda: 0', entonces tendría que cambiarlo a' lambda x, y, z: 0'. – unutbu

+0

1. ¿Qué es 'pass_func'? 2. ¿No podemos simplemente hacer 'lambda * args, ** kwargs: 0'? –

5

Creo que se puede manejar de una manera bastante pitonica usando decorators. (PEP 318, también)

+0

Gracias, eso resuelve mi primer problema. De hecho, pensé en decoradores, pero había olvidado que podía construir decoradores como deco (método), en lugar de @deco. Este último requiere modificar el código original. –

Cuestiones relacionadas