2009-09-23 14 views
9

Quiero crear una clase que ajuste otra clase para que cuando se ejecute una función a través de la clase contenedora también se ejecute una función pre y post. Quiero que la clase contenedora funcione con cualquier clase sin modificaciones.¿Cree una clase contenedora para llamar a una función pre y post sobre las funciones existentes?

Por ejemplo, si tengo esta clase.

class Simple(object): 
    def one(self): 
     print "one" 

    def two(self,two): 
     print "two" + two 

    def three(self): 
     print "three" 

pude utilizar de esta manera ...

number = Simple() 
number.one() 
number.two("2") 

hasta ahora he escrito esta clase contenedora ...

class Wrapper(object): 
    def __init__(self,wrapped_class): 
     self.wrapped_class = wrapped_class() 

    def __getattr__(self,attr): 
     return self.wrapped_class.__getattribute__(attr) 

    def pre(): 
     print "pre" 

    def post(): 
     print "post" 

Qué puedo llamar así ...

number = Wrapper(Simple) 
number.one() 
number.two("2") 

Cual se puede usar igual que el anterior ap arte de cambiar la primera línea.

Lo que quiero que ocurra es cuando se llama a una función a través de la clase contenedora se llama a la función pre de la clase contenedora y luego se llama a la función deseada en la clase envuelta y luego a la función post. Quiero poder hacer esto sin cambiar la clase empaquetada y también sin cambiar la forma en que se llaman las funciones, solo cambiando la sintaxis de cómo se crea la instancia de la clase. por ejemplo, número = (Simple) vs número = Paquete de oferta (simple)

Respuesta

20

Ya casi ha terminado, sólo tiene que hacer un poco de introspección dentro __getattr__, el retorno de una nueva función ajustada cuando el atributo original es exigible:

class Wrapper(object): 
    def __init__(self,wrapped_class): 
     self.wrapped_class = wrapped_class() 

    def __getattr__(self,attr): 
     orig_attr = self.wrapped_class.__getattribute__(attr) 
     if callable(orig_attr): 
      def hooked(*args, **kwargs): 
       self.pre() 
       result = orig_attr(*args, **kwargs) 
       # prevent wrapped_class from becoming unwrapped 
       if result == self.wrapped_class: 
        return self 
       self.post() 
       return result 
      return hooked 
     else: 
      return orig_attr 

    def pre(self): 
     print ">> pre" 

    def post(self): 
     print "<< post" 

Ahora, con este código:

number = Wrapper(Simple) 

print "\nCalling wrapped 'one':" 
number.one() 

print "\nCalling wrapped 'two':" 
number.two("2") 

El resultado es:

Calling wrapped 'one': 
>> pre 
one 
<< post 

Calling wrapped 'two': 
>> pre 
two2 
<< post 
+0

Esta es una respuesta agradable , pero no funciona si la clase envuelta tiene métodos que devuelven otra instancia de la clase envuelta. Esto no es hipotético, por ejemplo, es por eso que el paquete pandas es tan difícil de subclase, como he aprendido en los últimos días :) –

1

simplemente he notado en mi diseño original que no hay manera de pasar argumentos y kwargs a la clase envuelto, aquí está la respuesta actualizada a pasar las entradas a la función envuelta ...

class Wrapper(object): 
def __init__(self,wrapped_class,*args,**kargs): 
    self.wrapped_class = wrapped_class(*args,**kargs) 

def __getattr__(self,attr): 
    orig_attr = self.wrapped_class.__getattribute__(attr) 
    if callable(orig_attr): 
     def hooked(*args, **kwargs): 
      self.pre() 
      result = orig_attr(*args, **kwargs) 
      self.post() 
      return result 
     return hooked 
    else: 
     return orig_attr 

def pre(self): 
    print ">> pre" 

def post(self): 
    print "<< post"  
Cuestiones relacionadas