Chaînage de décorateurs et décorateurs paramétrés

PythonPythonBeginner
Pratiquer maintenant

This tutorial is from open-source community. Access the source code

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

Introduction

Dans ce laboratoire (lab), vous allez apprendre à utiliser les décorateurs (decorators) en Python, une fonctionnalité puissante qui peut modifier le comportement des fonctions et des méthodes. Les décorateurs sont couramment utilisés pour des tâches telles que la journalisation (logging), la mesure des performances, le contrôle d'accès et la vérification des types.

Vous allez apprendre à enchaîner plusieurs décorateurs, à créer des décorateurs qui acceptent des paramètres, à conserver les métadonnées des fonctions lors de l'utilisation de décorateurs et à appliquer des décorateurs à différents types de méthodes de classe. Les fichiers avec lesquels vous allez travailler sont logcall.py, validate.py et sample.py.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL python(("Python")) -.-> python/BasicConceptsGroup(["Basic Concepts"]) python(("Python")) -.-> python/FunctionsGroup(["Functions"]) python(("Python")) -.-> python/ObjectOrientedProgrammingGroup(["Object-Oriented Programming"]) python(("Python")) -.-> python/AdvancedTopicsGroup(["Advanced Topics"]) python/BasicConceptsGroup -.-> python/type_conversion("Type Conversion") python/FunctionsGroup -.-> python/function_definition("Function Definition") python/FunctionsGroup -.-> python/lambda_functions("Lambda Functions") python/FunctionsGroup -.-> python/scope("Scope") python/ObjectOrientedProgrammingGroup -.-> python/classes_objects("Classes and Objects") python/ObjectOrientedProgrammingGroup -.-> python/class_static_methods("Class Methods and Static Methods") python/AdvancedTopicsGroup -.-> python/decorators("Decorators") subgraph Lab Skills python/type_conversion -.-> lab-132515{{"Chaînage de décorateurs et décorateurs paramétrés"}} python/function_definition -.-> lab-132515{{"Chaînage de décorateurs et décorateurs paramétrés"}} python/lambda_functions -.-> lab-132515{{"Chaînage de décorateurs et décorateurs paramétrés"}} python/scope -.-> lab-132515{{"Chaînage de décorateurs et décorateurs paramétrés"}} python/classes_objects -.-> lab-132515{{"Chaînage de décorateurs et décorateurs paramétrés"}} python/class_static_methods -.-> lab-132515{{"Chaînage de décorateurs et décorateurs paramétrés"}} python/decorators -.-> lab-132515{{"Chaînage de décorateurs et décorateurs paramétrés"}} end

Conservation des métadonnées de fonction dans les décorateurs

En Python, les décorateurs (decorators) sont un outil puissant qui vous permet de modifier le comportement des fonctions. Cependant, lorsque vous utilisez un décorateur pour envelopper une fonction, il y a un petit problème. Par défaut, les métadonnées de la fonction d'origine, telles que son nom, sa chaîne de documentation (docstring) et ses annotations, sont perdues. Les métadonnées sont importantes car elles facilitent l'introspection (l'examen de la structure du code) et la génération de documentation. Vérifions d'abord ce problème.

Ouvrez votre terminal dans le WebIDE. Nous allons exécuter quelques commandes Python pour voir ce qui se passe lorsque nous utilisons un décorateur. Les commandes suivantes créeront une simple fonction add enveloppée dans un décorateur, puis afficheront la fonction et sa docstring.

cd ~/project
python3 -c "from logcall import logged; @logged
def add(x,y):
    'Adds two things'
    return x+y
    
print(add)
print(add.__doc__)"

Lorsque vous exécutez ces commandes, vous verrez une sortie similaire à ceci :

<function wrapper at 0x...>
None

Notez que au lieu d'afficher le nom de la fonction comme add, il affiche wrapper. Et la docstring, qui devrait être 'Adds two things', est None. Cela peut être un gros problème lorsque vous utilisez des outils qui dépendent de ces métadonnées, comme les outils d'introspection ou les générateurs de documentation.

Résolution du problème avec functools.wraps

Le module functools de Python vient à la rescousse. Il fournit un décorateur wraps qui peut nous aider à conserver les métadonnées de la fonction. Voyons comment nous pouvons modifier notre décorateur logged pour utiliser wraps.

  1. Tout d'abord, ouvrez le fichier logcall.py dans le WebIDE. Vous pouvez accéder au répertoire du projet en utilisant la commande suivante dans le terminal :
cd ~/project
  1. Maintenant, mettez à jour le décorateur logged dans logcall.py avec le code suivant. Le décorateur @wraps(func) est la clé ici. Il copie toutes les métadonnées de la fonction d'origine func vers la fonction enveloppante.
from functools import wraps

def logged(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper
  1. Le décorateur @wraps(func) a un rôle important. Il prend toutes les métadonnées (comme le nom, la docstring et les annotations) de la fonction d'origine func et les attache à la fonction wrapper. De cette façon, lorsque nous utilisons la fonction décorée, elle aura les bonnes métadonnées.

  2. Testons notre décorateur amélioré. Exécutez les commandes suivantes dans le terminal :

python3 -c "from logcall import logged; @logged
def add(x,y):
    'Adds two things'
    return x+y
    
print(add)
print(add.__doc__)"

Maintenant, vous devriez voir :

<function add at 0x...>
Adds two things

Génial ! Le nom de la fonction et la docstring sont conservés. Cela signifie que notre décorateur fonctionne maintenant comme prévu et que les métadonnées de la fonction d'origine sont intactes.

Correction du décorateur validate.py

Maintenant, appliquons la même correction au décorateur validated dans validate.py. Ce décorateur est utilisé pour valider les types des arguments de fonction et la valeur de retour en fonction des annotations de la fonction.

  1. Ouvrez validate.py dans le WebIDE.

  2. Mettez à jour le décorateur validated avec le décorateur @wraps. Le code suivant montre comment le faire. Le décorateur @wraps(func) est ajouté à la fonction wrapper à l'intérieur du décorateur validated pour conserver les métadonnées.

from functools import wraps

class Integer:
    @classmethod
    def __instancecheck__(cls, x):
        return isinstance(x, int)

def validated(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        ## Get function annotations
        annotations = func.__annotations__
        ## Check arguments against annotations
        for arg_name, arg_value in zip(func.__code__.co_varnames, args):
            if arg_name in annotations and not isinstance(arg_value, annotations[arg_name]):
                raise TypeError(f'Expected {arg_name} to be {annotations[arg_name].__name__}')

        ## Run the function and get the result
        result = func(*args, **kwargs)

        ## Check the return value
        if 'return' in annotations and not isinstance(result, annotations['return']):
            raise TypeError(f'Expected return value to be {annotations["return"].__name__}')

        return result
    return wrapper
  1. Testons que notre décorateur validated conserve maintenant les métadonnées. Exécutez les commandes suivantes dans le terminal :
python3 -c "from validate import validated, Integer; @validated
def multiply(x: Integer, y: Integer) -> Integer:
    'Multiplies two integers'
    return x * y
    
print(multiply)
print(multiply.__doc__)"

Vous devriez voir :

<function multiply at 0......>
Multiplies two integers

Maintenant, les deux décorateurs, logged et validated, conservent correctement les métadonnées des fonctions qu'ils décorent. Cela garantit que lorsque vous utilisez ces décorateurs, les fonctions auront toujours leurs noms, docstrings et annotations d'origine, ce qui est très utile pour la lisibilité et la maintenance du code.

✨ Vérifier la solution et pratiquer

Création de décorateurs avec des arguments

Jusqu'à présent, nous avons utilisé le décorateur @logged, qui affiche toujours un message fixe. Mais que faire si vous souhaitez personnaliser le format du message ? Dans cette section, nous allons apprendre à créer un nouveau décorateur qui peut accepter des arguments, vous offrant ainsi plus de flexibilité dans l'utilisation des décorateurs.

Compréhension des décorateurs paramétrés

Un décorateur paramétré est un type spécial de fonction. Au lieu de modifier directement une autre fonction, il retourne un décorateur. La structure générale d'un décorateur paramétré ressemble à ceci :

def decorator_with_args(arg1, arg2, ...):
    def actual_decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            ## Use arg1, arg2, ... here
            ## Call the original function
            return func(*args, **kwargs)
        return wrapper
    return actual_decorator

Lorsque vous utilisez @decorator_with_args(value1, value2) dans votre code, Python appelle d'abord decorator_with_args(value1, value2). Cet appel retourne le véritable décorateur, qui est ensuite appliqué à la fonction qui suit la syntaxe @. Ce processus en deux étapes est essentiel au fonctionnement des décorateurs paramétrés.

Création du décorateur logformat

Créons un décorateur @logformat(fmt) qui prend une chaîne de formatage comme argument. Cela nous permettra de personnaliser le message de journalisation.

  1. Ouvrez logcall.py dans le WebIDE et ajoutez le nouveau décorateur. Le code ci-dessous montre comment définir à la fois le décorateur logged existant et le nouveau décorateur logformat :
from functools import wraps

def logged(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

def logformat(fmt):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(fmt.format(func=func))
            return func(*args, **kwargs)
        return wrapper
    return decorator

Dans le décorateur logformat, la fonction externe logformat prend une chaîne de formatage fmt comme argument. Elle retourne ensuite la fonction decorator, qui est le véritable décorateur qui modifie la fonction cible.

  1. Maintenant, testons notre nouveau décorateur en modifiant sample.py. Le code suivant montre comment utiliser à la fois les décorateurs logged et logformat sur différentes fonctions :
from logcall import logged, logformat

@logged
def add(x, y):
    "Adds two numbers"
    return x + y

@logged
def sub(x, y):
    "Subtracts y from x"
    return x - y

@logformat('{func.__code__.co_filename}:{func.__name__}')
def mul(x, y):
    "Multiplies two numbers"
    return x * y

Ici, les fonctions add et sub utilisent le décorateur logged, tandis que la fonction mul utilise le décorateur logformat avec une chaîne de formatage personnalisée.

  1. Exécutez le fichier sample.py mis à jour pour voir les résultats. Ouvrez votre terminal et exécutez la commande suivante :
cd ~/project
python3 -c "import sample; print(sample.add(2, 3)); print(sample.mul(2, 3))"

Vous devriez voir une sortie similaire à :

Calling add
5
sample.py:mul
6

Cette sortie montre que le décorateur logged affiche le nom de la fonction comme prévu, et le décorateur logformat utilise la chaîne de formatage personnalisée pour afficher le nom du fichier et le nom de la fonction.

Redéfinition du décorateur logged en utilisant logformat

Maintenant que nous avons un décorateur logformat plus flexible, nous pouvons redéfinir notre décorateur logged d'origine en l'utilisant. Cela nous aidera à réutiliser le code et à maintenir un format de journalisation cohérent.

  1. Mettez à jour logcall.py avec le code suivant :
from functools import wraps

def logformat(fmt):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            print(fmt.format(func=func))
            return func(*args, **kwargs)
        return wrapper
    return decorator

## Define logged using logformat
logged = lambda func: logformat("Calling {func.__name__}")(func)

Ici, nous utilisons une fonction lambda pour définir le décorateur logged en termes du décorateur logformat. La fonction lambda prend une fonction func et applique le décorateur logformat avec une chaîne de formatage spécifique.

  1. Testez que le décorateur logged redéfini fonctionne toujours. Ouvrez votre terminal et exécutez la commande suivante :
cd ~/project
python3 -c "from logcall import logged; @logged
def greet(name):
    return f'Hello, {name}'
    
print(greet('World'))"

Vous devriez voir :

Calling greet
Hello, World

Cela montre que le décorateur logged redéfini fonctionne comme prévu, et nous avons réussi à réutiliser le décorateur logformat pour obtenir un format de journalisation cohérent.

✨ Vérifier la solution et pratiquer

Application de décorateurs aux méthodes de classe

Maintenant, nous allons explorer comment les décorateurs interagissent avec les méthodes de classe. Cela peut être un peu délicat car Python a différents types de méthodes : les méthodes d'instance, les méthodes de classe, les méthodes statiques et les propriétés. Les décorateurs sont des fonctions qui prennent une autre fonction et étendent le comportement de cette dernière sans la modifier explicitement. Lorsque nous appliquons des décorateurs aux méthodes de classe, nous devons faire attention à la façon dont ils fonctionnent avec ces différents types de méthodes.

Compréhension du défi

Voyons ce qui se passe lorsque nous appliquons notre décorateur @logged à différents types de méthodes. Le décorateur @logged est probablement utilisé pour enregistrer des informations sur les appels de méthode.

  1. Créez un nouveau fichier methods.py dans le WebIDE. Ce fichier contiendra notre classe avec différents types de méthodes décorées avec le décorateur @logged.
from logcall import logged

class Spam:
    @logged
    def instance_method(self):
        print("Instance method called")
        return "instance result"

    @logged
    @classmethod
    def class_method(cls):
        print("Class method called")
        return "class result"

    @logged
    @staticmethod
    def static_method():
        print("Static method called")
        return "static result"

    @logged
    @property
    def property_method(self):
        print("Property method called")
        return "property result"

Dans ce code, nous avons une classe Spam avec quatre types différents de méthodes. Chaque méthode est décorée avec le décorateur @logged, et certaines sont également décorées avec d'autres décorateurs intégrés comme @classmethod, @staticmethod et @property.

  1. Testons comment cela fonctionne. Nous allons exécuter une commande Python dans le terminal pour appeler ces méthodes et voir la sortie.
cd ~/project
python3 -c "from methods import Spam; s = Spam(); print(s.instance_method()); print(Spam.class_method()); print(Spam.static_method()); print(s.property_method)"

Lorsque vous exécutez cette commande, vous pourriez remarquer quelques problèmes :

  • Le décorateur @property peut ne pas fonctionner correctement avec notre décorateur @logged. Le décorateur @property est utilisé pour définir une méthode comme une propriété, et il a un mode de fonctionnement spécifique. Lorsqu'il est combiné avec le décorateur @logged, il pourrait y avoir des conflits.
  • L'ordre des décorateurs est important pour @classmethod et @staticmethod. L'ordre dans lequel les décorateurs sont appliqués peut changer le comportement de la méthode.

L'ordre des décorateurs

Lorsque vous appliquez plusieurs décorateurs, ils sont appliqués de bas en haut. Cela signifie que le décorateur le plus proche de la définition de la méthode est appliqué en premier, puis ceux au - dessus sont appliqués séquentiellement. Par exemple :

@decorator1
@decorator2
def func():
    pass

Cela équivaut à :

func = decorator1(decorator2(func))

Dans cet exemple, decorator2 est appliqué à func en premier, puis decorator1 est appliqué au résultat de decorator2(func).

Correction de l'ordre des décorateurs

Mettons à jour notre fichier methods.py pour corriger l'ordre des décorateurs. En changeant l'ordre des décorateurs, nous pouvons nous assurer que chaque méthode fonctionne comme prévu.

from logcall import logged

class Spam:
    @logged
    def instance_method(self):
        print("Instance method called")
        return "instance result"

    @classmethod
    @logged
    def class_method(cls):
        print("Class method called")
        return "class result"

    @staticmethod
    @logged
    def static_method():
        print("Static method called")
        return "static result"

    @property
    @logged
    def property_method(self):
        print("Property method called")
        return "property result"

Dans cette version mise à jour :

  • Pour instance_method, l'ordre n'a pas d'importance. Les méthodes d'instance sont appelées sur une instance de la classe, et le décorateur @logged peut être appliqué dans n'importe quel ordre sans affecter sa fonctionnalité de base.
  • Pour class_method, nous appliquons @classmethod après @logged. Le décorateur @classmethod change la façon dont la méthode est appelée, et en l'appliquant après @logged, nous nous assurons que la journalisation fonctionne correctement.
  • Pour static_method, nous appliquons @staticmethod après @logged. De même que pour @classmethod, le décorateur @staticmethod a son propre comportement, et l'ordre avec le décorateur @logged doit être correct.
  • Pour property_method, nous appliquons @property après @logged. Cela garantit que le comportement de la propriété est maintenu tout en obtenant la fonctionnalité de journalisation.
  1. Testons le code mis à jour. Nous allons exécuter la même commande que précédemment pour voir si les problèmes sont résolus.
cd ~/project
python3 -c "from methods import Spam; s = Spam(); print(s.instance_method()); print(Spam.class_method()); print(Spam.static_method()); print(s.property_method)"

Vous devriez maintenant voir une journalisation correcte pour tous les types de méthodes :

Calling instance_method
Instance method called
instance result
Calling class_method
Class method called
class result
Calling static_method
Static method called
static result
Calling property_method
Property method called
property result

Bonnes pratiques pour les décorateurs de méthode

Lorsque vous travaillez avec des décorateurs de méthode, suivez ces bonnes pratiques :

  1. Appliquez les décorateurs de transformation de méthode (@classmethod, @staticmethod, @property) après vos décorateurs personnalisés. Cela garantit que les décorateurs personnalisés peuvent effectuer leur journalisation ou d'autres opérations en premier, puis les décorateurs intégrés peuvent transformer la méthode comme prévu.
  2. Sachez que l'exécution du décorateur se produit au moment de la définition de la classe, pas au moment de l'appel de la méthode. Cela signifie que tout code de configuration ou d'initialisation dans le décorateur sera exécuté lorsque la classe est définie, pas lorsque la méthode est appelée.
  3. Pour les cas plus complexes, vous devrez peut - être créer des décorateurs spécialisés pour différents types de méthodes. Les différents types de méthodes ont des comportements différents, et un décorateur universel peut ne pas fonctionner dans toutes les situations.
✨ Vérifier la solution et pratiquer

Création d'un décorateur de vérification de type avec des arguments

Dans les étapes précédentes, nous avons appris à connaître le décorateur @validated. Ce décorateur est utilisé pour appliquer les annotations de type dans les fonctions Python. Les annotations de type sont un moyen de spécifier les types attendus pour les arguments et les valeurs de retour des fonctions. Maintenant, nous allons aller plus loin. Nous allons créer un décorateur plus flexible qui peut accepter des spécifications de type en tant qu'arguments. Cela signifie que nous pouvons définir les types que nous souhaitons pour chaque argument et la valeur de retour de manière plus explicite.

Compréhension de l'objectif

Notre objectif est de créer un décorateur @enforce(). Ce décorateur nous permettra de spécifier des contraintes de type en utilisant des arguments nommés. Voici un exemple de son fonctionnement :

@enforce(x=Integer, y=Integer, return_=Integer)
def add(x, y):
    return x + y

Dans cet exemple, nous utilisons le décorateur @enforce pour spécifier que les arguments x et y de la fonction add doivent être de type Integer, et que la valeur de retour doit également être de type Integer. Ce décorateur se comportera de manière similaire à notre décorateur @validated précédent, mais il nous donne plus de contrôle sur les spécifications de type.

Création du décorateur enforce

  1. Tout d'abord, ouvrez le fichier validate.py dans le WebIDE. Nous allons ajouter notre nouveau décorateur à ce fichier. Voici le code que nous allons ajouter :
from functools import wraps

class Integer:
    @classmethod
    def __instancecheck__(cls, x):
        return isinstance(x, int)

def validated(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        ## Get function annotations
        annotations = func.__annotations__
        ## Check arguments against annotations
        for arg_name, arg_value in zip(func.__code__.co_varnames, args):
            if arg_name in annotations and not isinstance(arg_value, annotations[arg_name]):
                raise TypeError(f'Expected {arg_name} to be {annotations[arg_name].__name__}')

        ## Run the function and get the result
        result = func(*args, **kwargs)

        ## Check the return value
        if 'return' in annotations and not isinstance(result, annotations['return']):
            raise TypeError(f'Expected return value to be {annotations["return"].__name__}')

        return result
    return wrapper

def enforce(**type_specs):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            ## Check argument types
            for arg_name, arg_value in zip(func.__code__.co_varnames, args):
                if arg_name in type_specs and not isinstance(arg_value, type_specs[arg_name]):
                    raise TypeError(f'Expected {arg_name} to be {type_specs[arg_name].__name__}')

            ## Run the function and get the result
            result = func(*args, **kwargs)

            ## Check the return value
            if 'return_' in type_specs and not isinstance(result, type_specs['return_']):
                raise TypeError(f'Expected return value to be {type_specs["return_"].__name__}')

            return result
        return wrapper
    return decorator

Analysons ce que fait ce code. La classe Integer est utilisée pour définir un type personnalisé. Le décorateur validated vérifie les types des arguments de la fonction et de la valeur de retour en fonction des annotations de type de la fonction. Le décorateur enforce est le nouveau que nous créons. Il prend des arguments nommés qui spécifient les types pour chaque argument et la valeur de retour. À l'intérieur de la fonction wrapper du décorateur enforce, nous vérifions si les types des arguments et de la valeur de retour correspondent aux types spécifiés. Sinon, nous levons une erreur TypeError.

  1. Maintenant, testons notre nouveau décorateur @enforce. Nous allons exécuter quelques cas de test pour voir s'il fonctionne comme prévu. Voici le code pour exécuter les tests :
cd ~/project
python3 -c "from validate import enforce, Integer

@enforce(x=Integer, y=Integer, return_=Integer)
def add(x, y):
    return x + y

## This should work
print(add(2, 3))

## This should raise a TypeError
try:
    print(add('2', 3))
except TypeError as e:
    print(f'Error: {e}')

## This should raise a TypeError
try:
    @enforce(x=Integer, y=Integer, return_=Integer)
    def bad_add(x, y):
        return str(x + y)
    print(bad_add(2, 3))
except TypeError as e:
    print(f'Error: {e}')"

Dans ce code de test, nous définissons d'abord une fonction add avec le décorateur @enforce. Nous appelons ensuite la fonction add avec des arguments valides, ce qui devrait fonctionner sans erreur. Ensuite, nous appelons la fonction add avec un argument invalide, ce qui devrait lever une erreur TypeError. Enfin, nous définissons une fonction bad_add qui retourne une valeur du mauvais type, ce qui devrait également lever une erreur TypeError.

Lorsque vous exécutez ce code de test, vous devriez voir une sortie similaire à ceci :

5
Error: Expected x to be Integer
Error: Expected return value to be Integer

Cette sortie montre que notre décorateur @enforce fonctionne correctement. Il lève une erreur TypeError lorsque les types des arguments ou de la valeur de retour ne correspondent pas aux types spécifiés.

Comparaison des deux approches

Les décorateurs @validated et @enforce atteignent le même objectif d'application de contraintes de type, mais ils le font de différentes manières.

  1. Le décorateur @validated utilise les annotations de type intégrées à Python. Voici un exemple :

    @validated
    def add(x: Integer, y: Integer) -> Integer:
        return x + y

    Avec cette approche, nous spécifions les types directement dans la définition de la fonction en utilisant des annotations de type. C'est une fonctionnalité intégrée à Python, et elle offre un meilleur support dans les environnements de développement intégré (IDE). Les IDE peuvent utiliser ces annotations de type pour fournir des complétions de code, des vérifications de type et d'autres fonctionnalités utiles.

  2. Le décorateur @enforce, en revanche, utilise des arguments nommés pour spécifier les types. Voici un exemple :

    @enforce(x=Integer, y=Integer, return_=Integer)
    def add(x, y):
        return x + y

    Cette approche est plus explicite car nous passons directement les spécifications de type en tant qu'arguments au décorateur. Elle peut être utile lorsque vous travaillez avec des bibliothèques qui reposent sur d'autres systèmes d'annotation.

Chaque approche a ses propres avantages. Les annotations de type sont une partie native de Python et offrent un meilleur support dans les IDE, tandis que l'approche @enforce nous donne plus de flexibilité et d'explicitation. Vous pouvez choisir l'approche qui convient le mieux à vos besoins en fonction du projet sur lequel vous travaillez.

✨ Vérifier la solution et pratiquer

Résumé

Dans ce laboratoire (lab), vous avez appris à créer et à utiliser efficacement les décorateurs. Vous avez appris à conserver les métadonnées des fonctions avec functools.wraps, à créer des décorateurs acceptant des paramètres, à gérer plusieurs décorateurs et à comprendre leur ordre d'application. Vous avez également appris à appliquer des décorateurs à différentes méthodes de classe et à créer un décorateur de vérification de type qui prend des arguments.

Ces modèles de décorateurs sont couramment utilisés dans les frameworks Python tels que Flask, Django et pytest. Maîtriser les décorateurs vous permettra d'écrire un code plus maintenable et réutilisable. Pour approfondir vos connaissances, vous pouvez explorer les gestionnaires de contexte (context managers), les décorateurs basés sur des classes, l'utilisation de décorateurs pour la mise en cache (caching) et la vérification avancée de type avec des décorateurs.