Xavier Lamorlette

Python : décorateurs

Sommaire :

Définition d'un décorateur

Un décorateur permet d'envelopper une fonction en la modifiant. Un décorateur prend une fonction en argument et retourne une fonction.

from functools import wraps

def print_arguments(function):
    @wraps(function)
    def wrapper(*args, **kwargs):
        print(f"Arguments: {args} {kwargs}")
        return function(*args, **kwargs)
    return wrapper

@print_arguments
def a_function(a, b): ...

L'utilisation, optionnelle, de wraps permet de conserver le nom et la docstring de la fonction lors de l'introspection du code.

Décorer une méthode en utilisant un membre

Via une classe de base

from typing import Callable
from functools import wraps

class ActorWithErrorCallback:
    _on_error: Callable[[Exception, str], None]

    def __init__(self,
                 on_error: Callable[[Exception, str], None]):
        self._on_error = on_error

    def _try_except(message: str):
        def decorator(function):
            @wraps(function)
            def wrapper(self, *args, **kwargs):
                try:
                    function(self, *args, **kwargs)
                except Exception as error:
                    self._on_error(error, message)
            return wrapper
        return decorator

class Actor(ActorWithErrorCallback):
    @ActorWithErrorCallback._try_except("Error while doing toto")
    def toto(self, a, b):
        print(f"Actor.toto(): a={a}, b={b}")
        raise RuntimeError("the error")

def on_error(error: Exception,
             message: str):
    print(f"{message}: {error}")

actor = Actor(on_error)
actor.toto(1, 2)

En redéfinissant la méthode

from typing import Callable
from functools import wraps

def try_except(on_error: Callable[[Exception, str], None],
               message: str):
    def decorator(function):
        @wraps(function)
        def wrapper(*args, **kwargs):
            try:
                function(*args, **kwargs)
            except Exception as error:
                on_error(error, message)
        return wrapper
    return decorator

class Actor:
    _on_error: Callable[[Exception, str], None]

    def __init__(self,
                 on_error: Callable[[Exception, str], None]):
        self._on_error = on_error
        self.toto = try_except(self._on_error, "Error while doing toto")(self.toto)

    def toto(self, a, b):
        print(f"Actor.toto(): a={a}, b={b}")
        raise RuntimeError("the error")

def on_error_callback(error: Exception,
                      message: str):
    print(f"{message}: {error}")

actor = Actor(on_error_callback)
actor.toto(1, 2)

La dernière mise à jour de cette page date d'avril 2023.

Le contenu de ce site est, en tant qu'œuvre originale de l'esprit, protégé par le droit d'auteur.
Pour tout commentaire, vous pouvez m'écrire à xavier.lamorlette@gmail.com.