Comment utiliser les décorateurs pour modifier le comportement des fonctions en Python

PythonPythonBeginner
Pratiquer maintenant

💡 Ce tutoriel est traduit par l'IA à partir de la version anglaise. Pour voir la version originale, vous pouvez cliquer ici

Introduction

Les décorateurs Python sont un outil puissant qui vous permet de modifier le comportement des fonctions sans changer leur fonctionnalité de base. Dans ce tutoriel, nous plongerons dans le monde des décorateurs et explorerons comment vous pouvez les utiliser pour améliorer les performances, la journalisation et la fonctionnalité globale de votre code Python.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python/FunctionsGroup -.-> python/function_definition("Function Definition") python/FunctionsGroup -.-> python/arguments_return("Arguments and Return Values") python/FunctionsGroup -.-> python/lambda_functions("Lambda Functions") python/AdvancedTopicsGroup -.-> python/decorators("Decorators") python/AdvancedTopicsGroup -.-> python/context_managers("Context Managers") subgraph Lab Skills python/function_definition -.-> lab-415777{{"Comment utiliser les décorateurs pour modifier le comportement des fonctions en Python"}} python/arguments_return -.-> lab-415777{{"Comment utiliser les décorateurs pour modifier le comportement des fonctions en Python"}} python/lambda_functions -.-> lab-415777{{"Comment utiliser les décorateurs pour modifier le comportement des fonctions en Python"}} python/decorators -.-> lab-415777{{"Comment utiliser les décorateurs pour modifier le comportement des fonctions en Python"}} python/context_managers -.-> lab-415777{{"Comment utiliser les décorateurs pour modifier le comportement des fonctions en Python"}} end

Comprendre les décorateurs Python

Les décorateurs Python sont une fonctionnalité puissante qui vous permet de modifier le comportement d'une fonction sans changer son code source. Ils sont un moyen d'envelopper une fonction avec une autre fonction, ajoutant des fonctionnalités supplémentaires à la fonction d'origine.

Qu'est-ce qu'un décorateur ?

Les décorateurs sont un moyen de modifier le comportement d'une fonction ou d'une classe. Ils sont définis à l'aide du symbole @, suivi de la fonction décoratrice, et placés juste avant la définition de la fonction ou de la classe.

Voici un exemple de fonction décoratrice simple :

def uppercase(func):
    def wrapper():
        result = func()
        return result.upper()
    return wrapper

@uppercase
def say_hello():
    return "hello"

print(say_hello())  ## Output: HELLO

Dans cet exemple, la fonction décoratrice uppercase prend une fonction func en argument et retourne une nouvelle fonction wrapper qui appelle func puis convertit le résultat en majuscules.

Comment les décorateurs fonctionnent-ils ?

Les décorateurs fonctionnent en modifiant le comportement d'une fonction à l'exécution. Lorsque vous appliquez un décorateur à une fonction, la fonction d'origine est remplacée par le résultat de la fonction décoratrice. Cela signifie que lorsque vous appelez la fonction décorée, vous appelez en fait la fonction wrapper que le décorateur a retournée.

Le processus d'application d'un décorateur peut être décomposé en les étapes suivantes :

  1. La fonction décoratrice est définie. Elle prend une fonction en argument et retourne une nouvelle fonction.
  2. Le symbole @ est utilisé pour appliquer le décorateur à une fonction.
  3. Lorsque la fonction décorée est appelée, la fonction wrapper retournée par le décorateur est exécutée à la place de la fonction d'origine.

Avantages de l'utilisation des décorateurs

Les décorateurs offrent plusieurs avantages :

  1. Réutilisation du code : Les décorateurs vous permettent de réutiliser la même fonctionnalité sur plusieurs fonctions, rendant votre code plus conforme au principe DRY (Don't Repeat Yourself - Ne vous répétez pas).
  2. Séparation des préoccupations : Les décorateurs vous aident à séparer la fonctionnalité principale d'une fonction de la fonctionnalité supplémentaire que vous souhaitez ajouter, rendant votre code plus modulaire et plus facile à maintenir.
  3. Flexibilité : Les décorateurs peuvent être facilement ajoutés ou supprimés d'une fonction, vous permettant d'activer ou de désactiver facilement certains comportements.
  4. Lisibilité : Les décorateurs rendent votre code plus lisible et auto-documenté, car le nom du décorateur indique clairement la fonctionnalité supplémentaire ajoutée à une fonction.

Cas d'utilisation courants des décorateurs

Les décorateurs peuvent être utilisés dans diverses situations, notamment :

  • Journalisation : Ajout de la fonctionnalité de journalisation à une fonction.
  • Mise en cache : Mise en cache des résultats d'une fonction pour améliorer les performances.
  • Authentification : Vérification si un utilisateur est autorisé à accéder à une fonction.
  • Mesure du temps : Mesure du temps d'exécution d'une fonction.
  • Gestion des erreurs : Fourniture d'une gestion d'erreurs personnalisée pour une fonction.

Dans la section suivante, nous explorerons comment appliquer des décorateurs pour modifier le comportement des fonctions en Python.

Appliquer des décorateurs pour modifier le comportement des fonctions

Maintenant que nous avons une compréhension de base de ce qu'est un décorateur et de son fonctionnement, explorons comment les utiliser pour modifier le comportement des fonctions en Python.

Passer des arguments aux décorateurs

Les décorateurs peuvent également accepter des arguments, ce qui vous permet de personnaliser leur comportement. Voici un exemple d'un décorateur qui prend un argument pour contrôler la casse de la sortie :

def case_converter(case):
    def decorator(func):
        def wrapper():
            result = func()
            if case == "upper":
                return result.upper()
            elif case == "lower":
                return result.lower()
            else:
                return result
        return wrapper
    return decorator

@case_converter("upper")
def say_hello():
    return "hello"

print(say_hello())  ## Output: HELLO

@case_converter("lower")
def say_goodbye():
    return "GOODBYE"

print(say_goodbye())  ## Output: goodbye

Dans cet exemple, le décorateur case_converter prend un argument case qui détermine si la sortie doit être convertie en majuscules ou en minuscules.

Décorer des fonctions avec des arguments

Les décorateurs peuvent également être utilisés pour modifier le comportement des fonctions qui prennent des arguments. Voici un exemple :

def log_function_call(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with args={args} and kwargs={kwargs}")
        return func(*args, **kwargs)
    return wrapper

@log_function_call
def add_numbers(a, b):
    return a + b

print(add_numbers(2, 3))  ## Output:
## Calling add_numbers with args=(2, 3) and kwargs={}
## 5

Dans cet exemple, le décorateur log_function_call enveloppe la fonction add_numbers et enregistre l'appel de fonction avant d'exécuter la fonction d'origine.

Empiler des décorateurs

Les décorateurs peuvent également être empilés, ce qui vous permet d'appliquer plusieurs décorateurs à une seule fonction. Voici un exemple :

def uppercase(func):
    def wrapper():
        result = func()
        return result.upper()
    return wrapper

def exclaim(func):
    def wrapper():
        result = func()
        return result + "!"
    return wrapper

@exclaim
@uppercase
def say_hello():
    return "hello"

print(say_hello())  ## Output: HELLO!

Dans cet exemple, la fonction say_hello est décorée à la fois avec le décorateur uppercase et le décorateur exclaim. Les décorateurs sont appliqués dans l'ordre dans lequel ils sont listés, donc le décorateur uppercase est appliqué en premier, et le décorateur exclaim est appliqué en second.

En utilisant des décorateurs, vous pouvez facilement modifier le comportement de vos fonctions sans changer leur fonctionnalité de base. Cela rend votre code plus modulaire, réutilisable et plus facile à maintenir.

Techniques et cas d'utilisation avancés des décorateurs

Comme vous l'avez vu, les décorateurs sont un outil puissant pour modifier le comportement des fonctions en Python. Dans cette section, nous explorerons quelques techniques et cas d'utilisation avancés des décorateurs.

Décorer des classes

Les décorateurs peuvent également être utilisés pour modifier le comportement des classes. Voici un exemple d'un décorateur qui ajoute une méthode de journalisation à une classe :

def log_class_methods(cls):
    class LoggedClass(cls):
        def __getattribute__(self, attr):
            if callable(super(LoggedClass, self).__getattribute__(attr)):
                def logged_method(*args, **kwargs):
                    print(f"Calling method {attr}")
                    return super(LoggedClass, self).__getattribute__(attr)(*args, **kwargs)
                return logged_method
            return super(LoggedClass, self).__getattribute__(attr)
    return LoggedClass

@log_class_methods
class MyClass:
    def __init__(self, value):
        self.value = value

    def do_something(self):
        print(f"Doing something with value: {self.value}")

obj = MyClass(42)
obj.do_something()  ## Output: Calling method do_something
                   ## Doing something with value: 42

Dans cet exemple, le décorateur log_class_methods prend une classe en argument et retourne une nouvelle classe qui enveloppe toutes les méthodes de la classe d'origine avec une fonction de journalisation.

Décorateurs avec état

Les décorateurs peuvent également maintenir un état entre les appels de fonction. Cela peut être utile pour la mise en cache, la limitation de débit ou d'autres opérations avec état. Voici un exemple d'un décorateur qui met en cache les résultats d'une fonction :

def cache(func):
    cache = {}
    def wrapper(*args):
        if args in cache:
            print("Returning cached result")
            return cache[args]
        else:
            result = func(*args)
            cache[args] = result
            return result
    return wrapper

@cache
def fibonacci(n):
    if n <= 1:
        return n
    else:
        return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(10))  ## Output: Calculating fibonacci(10)
                     ## 55
print(fibonacci(10))  ## Output: Returning cached result
                     ## 55

Dans cet exemple, le décorateur cache maintient un dictionnaire des arguments d'appel de fonction et de leurs résultats correspondants. Lorsque la fonction décorée est appelée, le décorateur vérifie d'abord si le résultat est déjà mis en cache, et si c'est le cas, il retourne le résultat mis en cache. Sinon, il calcule le résultat et le stocke dans le cache pour une utilisation future.

Usines de décorateurs

Parfois, vous pouvez vouloir créer des décorateurs qui peuvent être configurés avec des arguments. Cela peut être réalisé en utilisant une usine de décorateurs, qui est une fonction qui retourne un décorateur. Voici un exemple :

def repeat(n):
    def decorator(func):
        def wrapper():
            result = ""
            for _ in range(n):
                result += func()
            return result
        return wrapper
    return decorator

@repeat(3)
def say_hello():
    return "hello "

print(say_hello())  ## Output: hello hello hello

Dans cet exemple, la fonction repeat est une usine de décorateurs qui prend un argument n et retourne un décorateur qui enveloppe la fonction d'origine, l'appelant n fois et concaténant les résultats.

Ces techniques avancées de décorateurs démontrent la flexibilité et la puissance des décorateurs en Python. En utilisant des décorateurs, vous pouvez créer un code réutilisable, modulaire et facilement maintenable qui peut être facilement étendu et personnalisé pour répondre à vos besoins.

Résumé

À la fin de ce tutoriel, vous aurez une bonne compréhension des décorateurs Python et de la manière de les appliquer efficacement pour modifier le comportement des fonctions. Vous apprendrez des techniques avancées et explorerez des cas d'utilisation réels, ce qui vous permettra d'écrire un code Python plus efficace, plus lisible et plus facilement maintenable.