[{"data":1,"prerenderedAt":68},["ShallowReactive",2],{"qa-\u002Fpython\u002Ffunctions\u002Fdecorators":3},{"page":4,"siblings":52,"blog":65},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":20,"path":21,"questions":22,"related":44,"seo":45,"seoDescription":46,"stem":47,"subtopic":6,"topic":48,"topicSlug":49,"updated":50,"__hash__":51},"qa\u002Fpython\u002Ffunctions\u002Fdecorators.md","Decorators",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"hard","md","Python","python",{},true,1,"\u002Fpython\u002Ffunctions\u002Fdecorators",[23,28,32,36,40],{"id":24,"difficulty":25,"q":26,"a":27},"what-is-decorator","medium","What is a decorator and how does it work?","A **decorator** is a **callable that takes a function and returns a (usually\nwrapped) function**, letting you add behaviour **without modifying** the original.\nThe `@decorator` syntax above a `def` is just sugar for **reassigning the name** to\nthe decorator's result: `func = decorator(func)`.\n\n```python\ndef log_calls(func):\n    def wrapper(*args, **kwargs):    # accept any signature\n        print(f\"calling {func.__name__}\")\n        return func(*args, **kwargs) # delegate to the original\n    return wrapper\n\n@log_calls\ndef add(a, b):\n    return a + b\n# equivalent to: add = log_calls(add)\n\nadd(2, 3)   # prints \"calling add\", returns 5\n```\n\nThis works because functions are **first-class objects** — they can be passed\naround and returned. Decorators are the idiomatic way to factor out\n**cross-cutting concerns** (logging, timing, caching, access control).\n",{"id":29,"difficulty":14,"q":30,"a":31},"functools-wraps","Why should you use functools.wraps in a decorator?","Without it, the wrapper **replaces the original function's identity**: the\ndecorated object reports the **wrapper's** `__name__`, `__doc__`, signature, and\n`__module__`, which breaks introspection, debugging, and tools that rely on\nmetadata. **`functools.wraps`** copies that metadata from the original onto the\nwrapper.\n\n```python\nimport functools\n\ndef log_calls(func):\n    @functools.wraps(func)       # copy name, docstring, __wrapped__, etc.\n    def wrapper(*args, **kwargs):\n        return func(*args, **kwargs)\n    return wrapper\n\n@log_calls\ndef greet():\n    \"say hello\"\n    ...\n\ngreet.__name__   # \"greet\"  (without wraps -> \"wrapper\")\ngreet.__doc__    # \"say hello\"\n```\n\nIt also sets `__wrapped__`, so `inspect.signature` and unwrapping still work.\nRule of thumb: **always** apply `@functools.wraps(func)` to your wrapper — it's\neffectively free and prevents subtle bugs.\n",{"id":33,"difficulty":14,"q":34,"a":35},"decorator-with-arguments","How do you write a decorator that takes arguments?","You add **another layer of nesting**: an outer function takes the **decorator's\narguments** and returns the actual decorator, which takes the function and returns\nthe wrapper. So `@repeat(3)` first **calls** `repeat(3)` to get a decorator, which\nis then applied to the function.\n\n```python\nimport functools\n\ndef repeat(n):                       # takes the decorator argument\n    def decorator(func):             # takes the function\n        @functools.wraps(func)\n        def wrapper(*args, **kwargs):\n            for _ in range(n):\n                result = func(*args, **kwargs)\n            return result\n        return wrapper\n    return decorator\n\n@repeat(3)                           # repeat(3) returns 'decorator'\ndef ping():\n    print(\"pong\")\n```\n\nThe mental model: `@repeat(3)` is `ping = repeat(3)(ping)` — three calls deep.\nRemember the parentheses: `@repeat(3)` (with args) differs from `@repeat` (passing\nthe function directly), and forgetting them is a common bug.\n",{"id":37,"difficulty":14,"q":38,"a":39},"class-based-decorator","How do you implement a decorator as a class?","A class becomes a decorator by being **callable** — define **`__call__`**. The\n`__init__` receives the decorated function; `__call__` runs the wrapping logic on\neach invocation. This is handy when the decorator needs to **hold state** (like a\ncall count) in a clean, attribute-based way.\n\n```python\nimport functools\n\nclass CountCalls:\n    def __init__(self, func):\n        functools.update_wrapper(self, func)  # the class-based wraps\n        self.func = func\n        self.count = 0\n    def __call__(self, *args, **kwargs):\n        self.count += 1\n        print(f\"call #{self.count}\")\n        return self.func(*args, **kwargs)\n\n@CountCalls\ndef hello():\n    print(\"hi\")\n\nhello(); hello()      # \"call #1\" then \"call #2\"\nhello.count           # 2 — state lives on the instance\n```\n\nUse `functools.update_wrapper` (the function-form of `wraps`) to preserve\nmetadata. Class decorators shine for **stateful** decorators; for simple stateless\nones, a nested function with a `nonlocal` closure is usually lighter.\n",{"id":41,"difficulty":25,"q":42,"a":43},"stacking-decorators","When you stack multiple decorators, in what order do they apply?","Decorators apply **bottom-up** (nearest the function first) at **definition\ntime**, but the resulting wrappers **execute top-down** at **call time**. Stacking\nis just nested application: the top decorator wraps the result of the ones below\nit.\n\n```python\n@a\n@b\ndef f(): ...\n# equivalent to: f = a(b(f))   — b wraps first, a wraps outermost\n\ndef bold(fn):\n    return lambda: \"\u003Cb>\" + fn() + \"\u003C\u002Fb>\"\ndef italic(fn):\n    return lambda: \"\u003Ci>\" + fn() + \"\u003C\u002Fi>\"\n\n@bold\n@italic\ndef text():\n    return \"hi\"\n\ntext()   # \"\u003Cb>\u003Ci>hi\u003C\u002Fi>\u003C\u002Fb>\"  — bold is outer, runs around italic\n```\n\nSo the **closest** decorator is applied first but its logic runs **innermost**.\nOrder matters whenever decorators have side effects or transform results — e.g.\nput `@staticmethod` outermost, or `@app.route` above `@login_required` so auth\nruns before the view.\n",null,{"description":11},"Python interview questions on decorators, functools.wraps, decorators with arguments, class-based decorators, stacking order, and real-world use cases.","python\u002Ffunctions\u002Fdecorators","Functions","functions","2026-06-18","lkoUwwiWM-viu-zDimboEggNtVDCmWFyyrcd9HEU_xI",[53,54,57,61],{"subtopic":6,"path":21,"order":20},{"subtopic":55,"path":56,"order":12},"Function Arguments","\u002Fpython\u002Ffunctions\u002Farguments",{"subtopic":58,"path":59,"order":60},"Closures & Scope","\u002Fpython\u002Ffunctions\u002Fclosures",3,{"subtopic":62,"path":63,"order":64},"Lambdas & Higher-Order Functions","\u002Fpython\u002Ffunctions\u002Flambdas",4,{"path":66,"title":67},"\u002Fblog\u002Fpython-decorators-explained","Python Decorators Explained — Wrapping Functions, functools.wraps, and Decorators with Arguments",1781808676758]