2017-08-15 2 views
3

Je lisais sur les décorateurs et j'ai essayé de mélanger ces deux exemples et de les rendre décorateurs de classe au lieu des fonctions habituelles. Le premier vous permet seulement d'exécuter une fonction une fois par argument et le second compte combien de fois vous avez exécuté cette fonction. Ils fonctionnent tous les deux bien séparés mais quand j'essaye de décorer une fonction simple avec les deux en même temps ça échoue ... Ou n'échoue pas vraiment mais imprime un mauvais résultat inattendu. J'ai fait quelques lectures et j'ai trouvé que le module functools peut aider mais je ne sais pas comment.Comment utiliser ces décorateurs à deux classes avec functools.update_wrapper?

from functools import update_wrapper 

class Memoize: 
    def __init__(self, func): 
     self.func = func 
     self.memo = dict() 
     update_wrapper(self, func) 

    def __call__(self, *args): 
     if args not in self.memo: 
      self.memo[args] = self.func(args) 
     else: 
      print("cls decorator. You have printed this before") 
     return self.memo[args] 


class CallCounter: 
    def __init__(self, func): 
     self.func = func 
     self.calls = 0 
     self.__name__ = func.__name__ 
     update_wrapper(self, func) 

    def __call__(self, *args, **kwargs): 
     self.calls += 1 
     return self.func(*args, **kwargs) 


@Memoize 
@CallCounter 
def doubleprint(x): 
    for elem in x: 
     print(elem + " " + elem) 


doubleprint('Hello') 
doubleprint('Hello') 
doubleprint('Hello') 
doubleprint('Hello') 
doubleprint('Bye') 

print(doubleprint.calls) 

doubleprint('Hello') 

doubleprint('Hello') 
doubleprint('Hello') 
doubleprint('Hello') 
doubleprint('Bye') 

print(doubleprint.calls) 

Répondre

1

Par défaut update_wrapper met à jour la __dict__ d'une classe enveloppée. Donc, votre func en Memoize est en cours d'écrasement avec func en CallCounter ce qui signifie Memoize appelle directement votre doubleprint() fonction et n'appelant jamais CallCounter.

class Memoize: 
    def __init__(self, func): 
     self.func = func 
     print(type(self.func)) # <class '__main__.CallCounter'> 
     self.memo = dict() 
     update_wrapper(self, func) 
     print(type(self.func)) # <class 'function'> 

Vous pouvez résoudre ce problème en faisant:

 update_wrapper(self, func, updated=[]) 

qui ne copiera pas __dict__ du CallCounter dans l'instance Memoize mais sera toujours copier __name__, __doc__, etc.

Pour accéder à CallCounter classe vous le feriez:

print(doubleprint.__wrapped__.calls) 

Mais vous avez besoin du correctif ci-dessus ou cela affichera toujours 0 (parce qu'il n'est jamais appelé).