Decoratori Python explicați: de la @property la reîncercați logica în 15 minute (2026)
Decoratorii sunt caracteristica care separă „cunoaște Python” de „citește Python fluent”. Par magice, dar sunt mecanice: @decorator pe o funcție este exact f = decorator(f). Asta este. Orice altceva din acest tutorial este acea linie aplicată în diferite forme.
Teoria cu o singură linie
Cand scrii:
Python îl transformă în:
Acesta este totul mecanic. @decorator_name deasupra unui def rulează def_name = decorator_name(def_name) după definiție. Dacă interiorizezi acest lucru, decoratorii încetează să mai fie misterioși pentru totdeauna.
Scrierea primului tău decorator (60 de secunde)
Un decorator este orice apelabil care preia o funcție și returnează o funcție. Forma tipică:
*args, **kwargs din wrapper este un model defensiv - permite decoratorului să lucreze la orice funcție, indiferent de semnătură. Omiteți-l și veți obține TypeError: wrapper() acceptă 0 argumente poziționale, dar s-au dat 2 în momentul în care cineva decorează o funcție cu parametri.
Remedierea @functools.wraps
Un bug subtil: după decorare, add.__name__ este "wrapper", nu "add". Docstrings și indicii de tip dispar de asemenea. Remedierea este un singur import:
Regula: fiecare decorator pe care îl scrieți ar trebui să folosească `@functools.wraps(fn)`. Costă o linie și previne o întreagă categorie de dureri de depanare (introspecție, dispozitive pytest, formatare logging, pickle — toate se întrerup fără ea).
Cei patru decoratori pe care îi veți vedea în fiecare bază de cod
1. @property — atribute calculate
@property face o metodă apelabilă ca și cum ar fi un atribut. Perfect pentru valori derivate numai pentru citire. Există, de asemenea, @area.setter pentru calea de scriere, dar 90% din utilizările @property sunt doar pentru citire.
2. @staticmethod și @classmethod
@classmethod primește cls (clasa însăși) și este folosit pentru constructori alternativi. @staticmethod nu primește nimic special și este o funcție simplă care se întâmplă să trăiască în spațiul de nume al clasei.
3. @functools.cache — memorare
Unul dintre cei mai puternici decoratori stdlib. Memorează automat funcția pe baza argumentelor sale. Necesită ca toate argumentele să fie hashable (fără liste/dicte ca argumente - utilizați tupluri).
4. @dataclasses.dataclass — eliminarea boilerplate
@dataclass citește adnotările de tip la nivel de clasă și generează automat __init__, __repr__, __eq__ și (cu frozen=True) __hash__. Este cea mai mare îmbunătățire a calității vieții din Python 3.7+.
Scrierea unui decorator @retry (exemplu real)
Un decorator care reîncearcă o funcție la eșec cu backoff exponențial:
Observați cele trei funcții imbricate: retry acceptă argumentele de configurare și returnează decorator; decorator acceptă funcția și returnează wrapper; wrapper rulează bucla de reîncercare. Acest model cu trei straturi este așa cum arată orice decorator cu argumente - memorați forma, nu exemplul specific.
Decoratori bazați pe clasă (rari, dar utili)
Un decorator este orice apelabil. Cursurile cu __call__ funcționează și ele:
Utilizați atunci când decoratorul trebuie să păstreze starea care supraviețuiește unui singur apel (un contor, un cache, o găleată cu limită de rată).
Capcane comune
1. Uitând `@functools.wraps` — introspecția se întrerupe în tăcere.
2. Argumente implicite modificabile în decoratorul însuși — clasicul def wrapper(x, cache=[]) mușcă și aici.
3. Metode de decorare fără a accepta `self` — amintiți-vă că self este doar un argument pozițional; *args, **kwargs prinde bine.
4. Comandarea contează la stivuire: @a\n@b\ndef f() este f = a(b(f)). Citiți stivele de jos în sus.
Practică
Scrieți un decorator @timing care imprimă cât timp durează funcționarea funcției înfășurate, apoi aplicați-l unei funcții care calculează sum(range(10_000_000)). Întregul decorator este de 8 linii cu functools.wraps + time.perf_counter.
Odată ce interiorizați că @x este f = x(f), decoratorii devin una dintre cele mai productive caracteristici ale lui Python. Sunt peste tot în baze de cod reale: rute Flask/FastAPI (@app.route), fixtures pytest (@pytest.fixture), vizualizări Django (@login_required), validatoare Pydantic (@field_validator). Dacă înveți să le scrii singur, deblochează capacitatea de a citi pe fiecare dintre acestea.
Următorul pas: 10 lecții practice de decorator — construiți „@retry”, „@memoise”, „@rate_limit” și „@requires_auth” de la zero cu Python real rulând în browser.