Les décorateurs Python expliqués : de @property à réessayer la logique en 15 minutes (2026)
Les décorateurs sont la fonctionnalité qui sépare « connaît Python » de « lit Python couramment ». Ils ont l'air magiques, mais ils sont mécaniques : @decorator sur une fonction est exactement f = decorator(f). C'est ça. Tout le reste dans ce didacticiel consiste en une seule ligne appliquée sous différentes formes.
La théorie d'une seule ligne
Quand tu écris :
Python le transforme en :
C'est tout le mécanicien. @decorator_name au-dessus d'un def exécute def_name = decorator_name(def_name) après la définition. Si vous internalisez cela, les décorateurs cesseront d’être mystérieux pour toujours.
Écrire votre premier décorateur (60 secondes)
Un décorateur est tout appelable qui prend une fonction et renvoie une fonction. La forme typique :
Le *args, **kwargs dans le wrapper est un modèle défensif — il permet au décorateur de travailler sur n'importe quelle fonction, quelle que soit la signature. Ignorez-le et vous obtiendrez « TypeError : wrapper() prend 0 argument de position mais 2 ont été donnés » au moment où quelqu'un décore une fonction avec des paramètres.
Le correctif @functools.wraps
Un bug subtil : après la décoration, add.__name__ est "wrapper", pas "add". Les docstrings et les indices de type disparaissent également. Le correctif est une importation :
Règle : chaque décorateur que vous écrivez doit utiliser `@functools.wraps(fn)`. Cela coûte une ligne et évite toute une catégorie de problèmes de débogage (introspection, appareils « pytest », formateurs « logging », « pickle » — tous s'arrêtent silencieusement sans cela).
Les quatre décorateurs que vous verrez dans chaque base de code
1. @property — attributs calculés
@property rend une méthode appelable comme s'il s'agissait d'un attribut. Parfait pour les valeurs dérivées en lecture seule. Il y a aussi @area.setter pour le chemin d'écriture, mais 90 % des utilisations de @property sont en lecture seule.
2. @staticmethod et @classmethod
@classmethod obtient cls (la classe elle-même) et est utilisé pour les constructeurs alternatifs. @staticmethod n'a rien de spécial et est une fonction simple qui se trouve simplement à l'intérieur de l'espace de noms de la classe.
3. @functools.cache — mémorisation
L'un des décorateurs stdlib les plus puissants. Mémorise automatiquement la fonction en fonction de ses arguments. Nécessite que tous les arguments soient hachables (pas de listes/dictés comme arguments – utilisez des tuples).
4. @dataclasses.dataclass — élimination passe-partout
@dataclass lit les annotations de type au niveau de la classe et génère __init__, __repr__, __eq__ et (avec frozen=True) __hash__ automatiquement. Il s’agit de la plus grande amélioration de la qualité de vie de Python 3.7+.
Écrire un décorateur @retry (exemple concret)
Un décorateur qui réessaye une fonction en cas d'échec avec une interruption exponentielle :
Notez les trois fonctions imbriquées : retry accepte les arguments de configuration et renvoie decorator ; decorator accepte la fonction et renvoie wrapper ; wrapper exécute la boucle de nouvelle tentative. Ce modèle à trois couches est à quoi ressemble tout décorateur avec arguments : mémorisez la forme, pas l'exemple spécifique.
Décorateurs basés sur les classes (rares mais utiles)
Un décorateur est n’importe quel appelable. Les classes avec __call__ fonctionnent aussi :
À utiliser lorsque le décorateur doit conserver un état qui survit à un seul appel (un compteur, un cache, un compartiment de limite de débit).
Pièges courants
1. Oublier `@functools.wraps` — l'introspection s'interrompt silencieusement.
2. Arguments par défaut modifiables dans le décorateur lui-même — le bug classique def wrapper(x, cache=[]) se produit ici aussi.
3. Méthodes de décoration sans accepter « self » — rappelez-vous que « self » n'est qu'un argument de position ; *args, **kwargs le comprend bien.
4. L'ordre est important lors de l'empilement : @a\n@b\ndef f() est f = a(b(f)). Lisez les piles de bas en haut.
Pratique
Écrivez un décorateur @timing qui imprime le temps d'exécution de la fonction encapsulée, puis appliquez-le à une fonction qui calcule sum(range(10_000_000)). L'ensemble du décorateur est composé de 8 lignes avec functools.wraps + time.perf_counter.
Une fois que vous avez internalisé le fait que @x est f = x(f), les décorateurs deviennent l'une des fonctionnalités les plus productives de Python. Ils sont partout dans les bases de code réelles : routes Flask/FastAPI (@app.route), appareils pytest (@pytest.fixture), vues Django (@login_required), validateurs Pydantic (@field_validator). Apprendre à les écrire vous-même débloque la capacité de lire chacun d'entre eux.
Étape suivante : 10 leçons pratiques de décorateur — créez @retry, @memoise, @rate_limit et @requires_auth à partir de zéro avec du vrai Python exécuté dans votre navigateur.