Je souhaite créer un décorateur pour les fonctions de classe qui produira des messages de journalisation au début et à la fin des fonctions avec des informations spécifiques à l'instance de classe exécutant la fonction. J'ai essayé de le faire en créant le décorateur en tant que membre de la classe, mais cela ne fonctionne pas car le décorateur attend la fonction comme premier argument, mais en tant que méthode de classe, elle doit être self. Voici ce que j'ai écrit jusqu'à présent qui ne fonctionne pas :

import functools, logging

logging.basicConfig(format='%(asctime)s - %(levelname)s - %(funcName)s:%(message)s', 
                            datefmt='%Y-%m-%d %I:%M:%S %p',
                            level=logging.DEBUG)

class Foo:
    def __init__(self, bar):
        self.bar = bar
        
    def log(func):
        @functools.wraps(func)
        def wrapper_log(*args, **kwargs):
            logging.info(f"Started (instance {self.bar})")
            func(*args, **kwargs)
            logging.info(f"Finished (instance {self.bar}")
            return func(*args, **kwargs)
        return wrapper_log
        
    @log
    def test(self, a, b, c):
        pass
    
foo = Foo("bar")
foo.test(1, "b", [3, 4])

Je ne peux pas le sortir de la classe et passer l'instance de classe comme argument en décorant les fonctions de classe avec @log(self) car self n'existe pas en dehors des fonctions. Comment puis-je faire ceci?

0
Colin 6 oct. 2020 à 00:21

1 réponse

Meilleure réponse

Vous pouvez définir le décorateur en dehors de la classe et cela fonctionnera très bien, mais vous devez vous référer explicitement à self dans la signature du wrapper.

La raison pour laquelle ce travail est parce que toutes les définitions à l'intérieur d'une fonction ne sont évaluées que lorsque la fonction est appelée. Au moment où la fonction test est appelée, elle a déjà été liée à l'instance et self existera dans son espace de noms.

import functools, logging

logging.basicConfig(format='%(asctime)s - %(levelname)s - %(funcName)s:%(message)s', 
                            datefmt='%Y-%m-%d %I:%M:%S %p',
                            level=logging.DEBUG)

def log(func):
    @functools.wraps(func)
    def wrapper_log(self, *args, **kwargs):
        logging.info(f"Started (instance {self.bar})")
        func(self, *args, **kwargs)
        logging.info(f"Finished (instance {self.bar}")
        return func(self, *args, **kwargs)
    return wrapper_log

class Foo:
    def __init__(self, bar):
        self.bar = bar
        
    @log
    def test(self, a, b, c):
        pass
    
foo = Foo("bar")
foo.test(1, "b", [3, 4])

Cela produira

2020-10-05 11:31:20 PM - INFO - wrapper_log:Started (instance bar)
2020-10-05 11:31:20 PM - INFO - wrapper_log:Finished (instance bar
2
gioxc88 5 oct. 2020 à 21:43