Comment créer et utiliser des décorateurs en Python

Apprenez à écrire et appliquer des décorateurs en Python. Maîtrisez les fonctions d'encapsulation, modifiez le comportement, gérez les arguments et préservez les métadonnées avec des encapsulations.

Essayez cette solution dans l'éditeur

Aperçu

Les décorateurs sont l'une des fonctionnalités syntaxiques les plus élégantes et les plus puissantes de Python, vous permettant de modifier le comportement d'une fonction ou d'une classe sans changer définitivement son code. Ils sont largement utilisés pour des problèmes transversaux tels que la journalisation des temps d'exécution, l'application de l'authentification des utilisateurs, la mise en cache des résultats (mémorisation) et l'audit des points de terminaison de l'API.

Sous le capot, un décorateur est une fonction d'ordre supérieur qui prend une autre fonction comme argument, définit une fonction wrapper imbriquée qui injecte une logique personnalisée et renvoie cette fonction wrapper. Python fournit une syntaxe abrégée claire utilisant le symbole « @ » pour appliquer des décorateurs aux fonctions, les rendant visuellement élégantes.

Lorsque vous écrivez des décorateurs, vous souhaiterez généralement que la fonction décorée prenne en charge un nombre illimité d'arguments de position et de mots-clés. Vous pouvez y parvenir en concevant la fonction wrapper imbriquée pour accepter `*args` et `**kwargs` et les transmettre à la fonction cible. De plus, vous devez utiliser « functools.wraps » sur la fonction wrapper pour conserver les métadonnées de docstring et de nom de la fonction d'origine.

Sortie de code et d'exécution

Ce script montre l'écriture d'un décorateur de journalisation et l'encapsulation d'une fonction pour inspecter les entrées, les sorties et les métadonnées.

decorators_tutorial.py
Essayez dans l'éditeur
from functools import wraps

# 1. Defining a decorator
def log_execution(func):
    @wraps(func) # Preserves original function metadata
    def wrapper(*args, **kwargs):
        print(f"[Log] Calling function: {func.__name__} with args: {args}")
        result = func(*args, **kwargs)
        print(f"[Log] Finished {func.__name__}. Result: {result}")
        return result
    return wrapper

# 2. Applying the decorator using @ syntax
@log_execution
def add_numbers(a, b):
    """Adds two numbers and returns the sum."""
    return a + b

# 3. Invoking the decorated function
sum_result = add_numbers(15, 25)
print("Sum Result:", sum_result)

# Verifying metadata preservation
print("Docstring preserved:", add_numbers.__doc__)
Sortie terminale
[Log] Calling function: add_numbers with args: (15, 25)
[Log] Finished add_numbers. Result: 40
Sum Result: 40
Docstring preserved: Adds two numbers and returns the sum.

Mise en œuvre étape par étape

  • Définissez une fonction de décorateur externe qui prend la fonction cible comme argument.
  • Importez et appliquez @functools.wraps(func) sur votre fonction wrapper imbriquée pour protéger les métadonnées d'origine.
  • Implémentez wrapper(*args, **kwargs) pour capturer les variables d'entrée, injecter de la logique et appeler la fonction cible.
  • Renvoie l’objet fonction wrapper du bloc d’en-tête du décorateur.
  • Appliquez le décorateur en utilisant @decorator_name directement au-dessus de la signature de la fonction cible.

Foire aux questions

Pourquoi @functools.wraps est-il nécessaire chez les décorateurs ?

Sans wraps, la fonction décorée perd son identité d'origine (ses attributs __name__ et __doc__ sont remplacés par ceux de la fonction wrapper imbriquée), ce qui rend le débogage et l'introspection du code difficiles.

Puis-je appliquer plusieurs décorateurs à une seule fonction ?

Oui ! Vous pouvez enchaîner les décorateurs en empilant plusieurs lignes @. Python les applique de bas en haut (le plus proche de la définition de fonction s'exécute en premier).

Sujets connexes

Ressources Python recommandées

Développez vos connaissances avec des didacticiels interactifs, des aide-mémoire et des comparaisons de codes associés.