1.3.8 · HinglishPython Intermediate

Decorators — function decorators, @, wraps

1,579 words7 min readRead in English

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 karo

Inner 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 wrapper

return result KYUN? Agar hum bhool jaate hain, toh wrapped function silently None return karta hai — yeh ek classic bug hai.

Figure — Decorators — function decorators, @, wraps

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 wrapper

Worked 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?
Ek callable (usually ek inner wrapper) jo original function ki jagah le.
Wrapper mein *args, **kwargs KYUN use karte hain?
Taaki wrapper koi bhi arguments forward kare, har function signature ke liye kaam kare.
functools.wraps kya karta hai?
Original se wrapper pe metadata (__name__, __doc__, etc.) copy karta hai taaki introspection phir bhi kaam kare.
return func(...) bhoolna kyun cheezein tod deta hai?
Wrapper 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?
Inner wrapper enclosing func variable ke upar close ho jaata hai (ek closure).

Connections

Concept Map

violates

solved by

enables

desugars to

defines

built with

uses

must forward

must

loses metadata

applied to

restores

First-class functions

Need to add behaviour without editing bodies

Avoid repeated code / DRY

Decorator: callable taking callable returns callable

@deco sugar

f = deco of f

Inner wrapper function

Closure remembers func

Forward args, kwargs

Return original result

functools.wraps

Preserves __name__ and __doc__