Objašnjenje dekoratora Pythona: od @property do logike ponovnog pokušaja u 15 minuta (2026.)
Dekorateri su značajka koja odvaja "zna Python" od "tečno čita Python". Izgledaju čarobno, ali su mehanički: @decorator na funkciji je točno f = decorator(f). To je to. Sve ostalo u ovom vodiču je ta jedna linija primijenjena u različitim oblicima.
Teorija jedne linije
Kad pišeš:
Python ga pretvara u:
To je cijela mehanika. @decorator_name iznad def pokreće def_name = decorator_name(def_name) nakon definicije. Ako ovo internalizirate, dekorateri zauvijek prestaju biti tajanstveni.
Pisanje vašeg prvog dekoratera (60 sekundi)
Dekorator je bilo koji poziv koji preuzima funkciju i vraća je. Tipičan oblik:
*args, **kwargs u ovojnici obrambeni je uzorak — omogućuje dekorateru da radi na bilo kojoj funkciji bez obzira na potpis. Preskočite ga i dobit ćete "TypeError: wrapper() uzima 0 pozicijskih argumenata, ali su dana 2" u trenutku kada netko ukrasi funkciju parametrima.
Popravak @functools.wraps
Jedna suptilna greška: nakon ukrašavanja, add.__name__ je "wrapper", a ne "add". Dokumentacijski nizovi i tipski savjeti također nestaju. Popravak je jedan uvoz:
Pravilo: svaki dekorater koji pišete treba koristiti `@functools.wraps(fn)`. Košta jedan redak i sprječava čitavu kategoriju problema s ispravljanjem pogrešaka (introspekcija, pytest fixtures, logging formatteri, pickle — svi se tiho kvare bez njega).
Četiri dekoratera koja ćete vidjeti u svakoj bazi koda
1. @property — izračunati atributi
@property čini metodu pozivnom kao da je atribut. Savršeno za izvedene vrijednosti samo za čitanje. Postoji i @area.setter za stazu pisanja, ali 90% upotrebe @property je samo za čitanje.
2. @staticmethod i @classmethod
@classmethod dobiva cls (samu klasu) i koristi se za alternativne konstruktore. @staticmethod ne dobiva ništa posebno i obična je funkcija koja slučajno živi unutar imenskog prostora klase.
3. @functools.cache — memoriranje
Jedan od najmoćnijih stdlib dekoratora. Automatski memoizira funkciju na temelju njezinih argumenata. Zahtijeva da se svi argumenti mogu raspršiti (nema popisa / diktata kao argumenata — koristite torke).
4. @dataclasses.dataclass — eliminacija standardne ploče
@dataclass čita bilješke tipa na razini klase i automatski generira __init__, __repr__, __eq__ i (s frozen=True) __hash__. To je najveće pojedinačno poboljšanje kvalitete života u Python 3.7+.
Pisanje dekoratera @retry (primjer iz stvarnog svijeta)
Dekorater koji ponovno pokušava funkciju u slučaju kvara s eksponencijalnim odmakom:
Primijetite tri ugniježđene funkcije: retry prihvaća config argumente i vraća decorator; dekorator prihvaća funkciju i vraća omot; wrapper pokreće petlju ponovnog pokušaja. Ovaj troslojni uzorak je ono što svaki dekorater-s-argumentima izgleda - zapamtite oblik, a ne određeni primjer.
Dekorateri bazirani na klasi (rijetki, ali korisni)
Dekorater se može pozvati. Klase s __call__ također rade:
Koristite kada dekorater treba zadržati stanje koje nadživljava jedan poziv (brojač, predmemorija, spremnik s ograničenjem brzine).
Uobičajene zamke
1. Zaboravljanje `@functools.wraps` — introspekcija se tiho prekida.
2. Promjenjivi zadani argumenti u samom dekoratoru — klasični def wrapper(x, cache=[]) grize greške i ovdje.
3. Ukrašavanje metoda bez prihvaćanja `self` — zapamtite self je samo pozicijski argument; *args, **kwargs dobro hvata.
4. Redoslijed je važan pri slaganju: @a\n@b\ndef f() je f = a(b(f)). Čitajte nizove odozdo prema gore.
Vježbajte
Napišite dekorater "@timing" koji ispisuje koliko je vremena potrebno da se obavi omotana funkcija, a zatim ga primijenite na funkciju koja izračunava "sum(range(10_000_000))". Cijeli dekorater sastoji se od 8 redaka s functools.wraps + time.perf_counter.
Jednom kada shvatite da je @x f = x(f), dekorateri postaju jedna od najproduktivnijih značajki Pythona. Posvuda su u stvarnim bazama koda: Flask/FastAPI rute (@app.route), pytest fixture (@pytest.fixture), Django prikazi (@login_required), Pydantic validatori (@field_validator). Ako ih sami naučite pisati, otključavate sposobnost čitanja svakog od njih.
Sljedeći korak: 10 praktičnih lekcija dekoratera — izgradite @retry, @memoise, @rate_limit i @requires_auth ispočetka sa pravim Pythonom koji radi u vašem pregledniku.