2009-09-23 5 views
9

Je veux créer une classe qui enveloppe une autre classe de sorte que lorsqu'une fonction est exécutée par la classe wrapper, une fonction pre et post est également exécutée. Je veux que la classe wrapper fonctionne avec n'importe quelle classe sans modification.Créer une classe wrapper pour appeler une fonction pre et post autour de fonctions existantes?

Par exemple si j'ai cette classe.

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

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

    def three(self): 
     print "three" 

Je pourrais l'utiliser comme ça ...

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

J'ai écrit jusqu'à présent cette classe d'emballage ...

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" 

Ce que je peux appeler comme ça ...

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

Qui peut être utilisé comme ci-dessus ap l'art de changer la première ligne. Ce que je veux arriver c'est quand j'appelle une fonction à travers la classe wrapper, la fonction pre de la classe wrapper est appelée puis la fonction désirée dans la classe enveloppée puis la fonction post. Je veux pouvoir faire ceci sans changer la classe enveloppée et aussi sans changer la façon dont les fonctions sont appelées, changeant seulement la syntaxe de la façon dont l'instance de la classe est créée. par exemple le nombre = (Simple) par rapport au nombre = Wrapper (Simple)

Répondre

20

Vous y êtes presque, vous avez juste besoin de faire une introspection dans __getattr__, retournant une nouvelle fonction enveloppée lorsque l'attribut d'origine est appelable:

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" 

maintenant, avec ce code:

number = Wrapper(Simple) 

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

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

Le résultat est:

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

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

Ceci est une belle réponse , mais cela ne fonctionne pas si la classe enveloppée a des méthodes qui retournent une autre instance de la classe enveloppée. Ce n'est pas hypothétique - par exemple, c'est pourquoi le paquet pandas est si difficile à sous-classer, comme je l'ai appris ces derniers jours :) –

1

Je viens de remarquer dans mon design original il n'y a aucun moyen de passer args et kwargs à la classe enveloppée, voici la réponse mise à jour pour passer les entrées à la fonction enveloppée ...

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"  
Questions connexes