Decorators — function decorators, @, wraps
1.3.8· Coding › Python Intermediate
Decorators KYUN exist karte hain?
Tum hamesha existing functions ke existing bodies edit kiye bina unke aas-paas behaviour add karna chahte ho: timing, logging, caching, access-checks, retries.
Seedha fix yeh hai ki wahi start = time(); ...; print(elapsed) lines har function mein paste karo. Yeh DRY violate karta hai aur iska matlab hai ki agar tum logging format change karo toh 50 edits karne padenge.
Decorator PRECISELY kya hota hai?
Toh yeh dono identical hain:
@deco
def f(): ...def f(): ...
f = deco(f)Scratch se banana HOW (derivation)
Goal: ek timer jo print kare ki koi bhi function kitna time leta hai.
Step 1 — timer ko kya accept aur return karna chahiye?
Definition ke hisaab se ise original function func accept karna hai aur ek replacement function return karna hai. Kyun? Kyunki @timer ke baad, naam ab us cheez ki taraf point karta hai jo timer return karta hai.
def timer(func): # function leta hai
def wrapper(): # replacement
...
return func() # original ko phir bhi call karta hai
return wrapper # replacement wapas karoInner wrapper KYUN? Hume ek jagah chahiye jahan func() ke aas-paas extra code run ho sake. Inner function func ko closure ke zariye "yaad" rakhta hai.
Step 2 — koi bhi arguments forward karo.
Original kuch args le sakta hai. Wrapper ko unhe aage pass karna chahiye, toh *args, **kwargs use karo. Kyun? Taaki ek decorator har signature ke liye kaam kare.
def timer(func):
def wrapper(*args, **kwargs):
import time
start = time.perf_counter()
result = func(*args, **kwargs) # return value capture aur forward karo
print(f"{func.__name__} took {time.perf_counter()-start:.4f}s")
return result # KYUN: original ki value return karni chahiye
return wrapperreturn result KYUN? Agar hum bhool jaate hain, toh wrapped function silently None return karta hai — yeh ek classic bug hai.

functools.wraps KYUN?
Wrapping ke baad, f.__name__ "wrapper" ban jaata hai aur f.__doc__ kho jaata hai — kyunki naam ab inner function ki taraf point karta hai. Yeh help(), debuggers, aur introspection ko tod deta hai.
from functools import wraps
def timer(func):
@wraps(func) # <-- func ki identity wrapper pe copy karta hai
def wrapper(*args, **kwargs):
...
return wrapperWorked examples
Common mistakes
Forecast-then-Verify
Recall Output predict karo, phir check karo
def deco(f):
def w(*a, **k):
return f(*a, **k) * 2
return w
@deco
def g(x): return x + 1
print(g(4))Forecast: g(4) → original deta hai 5, doubled → 10. (Desugar: g = deco(g).)
Recall Feynman: 12-saal ke bacche ko samjhao
Socho tumhare paas ek toy robot hai jo ek number bolta hai. Decorator aise hai jaise use ek fancy box ke andar rakhna. Box robot ko phir bhi uska number bolne deta hai, lekin box saath mein har baar taali bhi bajata hai, ya use do baar repeat karta hai. Tumne robot ko nahi badla — tumne use wrap kiya. @ bas box pe ek sticker hai jo kehta hai "yeh wrapper use karo." Aur wraps robot ka naam ka label box ke bahar rakhna hai taaki tum pehchano ki andar kaun sa robot hai.
Flashcards
def f ke upar @deco kis mein desugar hota hai?
f = deco(f) — naam ko deco ki return value se rebind karta hai.Ek decorator ko kya return karna chahiye?
wrapper) jo original function ki jagah le.Wrapper mein *args, **kwargs KYUN use karte hain?
functools.wraps kya karta hai?
__name__, __doc__, etc.) copy karta hai taaki introspection phir bhi kaam kare.return func(...) bhoolna kyun cheezein tod deta hai?
None return karta hai, toh wrapped function silently apni return value kho deta hai.@repeat(3) kaise desugar hota hai?
f = repeat(3)(f) — factory pehle call hoti hai, actual decorator return karti hai.Stacked @a over @b over def f kaise evaluate hota hai?
f = a(b(f)) — bottom (def ke paas) pehle apply hota hai.Closures func ko "yaad" rakhne mein kya enable karta hai?
func variable ke upar close ho jaata hai (ek closure).Connections
- Closures and free variables
- First-class functions
- functools — wraps, lru_cache, partial
- Higher-order functions
- Class decorators and property
- args and kwargs unpacking