← Home ← Codex ← DEBT ← Engine
Browse by Category
+ added · ✎ updated 7d
← Back to glossary

Python Decorators

Python Python 2.4+ Intermediate
debt(d5/e1/b3/t5)
d5 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'specialist tool catches' (d5), pylint/ruff have rules (e.g. ruff's B902-related, pylint's missing functools.wraps detection) that flag decorators without @functools.wraps, but subtler issues like argument handling or stateful decorators remain silent until runtime.

e1 Effort Remediation debt — work required to fix once spotted

Closest to 'one-line patch' (e1), the quick_fix is literally adding @functools.wraps(func) above the wrapper — a single-line addition per decorator.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3), decorators are typically scoped to specific cross-cutting concerns (auth, logging, caching) and don't define the system's shape, though heavy decorator stacking in one component adds a maintenance tax there.

t5 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'notable trap' (t5), the misconception that decorators modify functions in place rather than replacing them is a documented gotcha most Python devs eventually learn; losing __name__/__doc__ silently is the canonical surprise.

About DEBT scoring →

Also Known As

Python decorator @decorator syntax function decorator

TL;DR

Functions that wrap other functions to add behaviour — @cache, @dataclass, @property — applied at definition time with the @ syntax.

Explanation

A decorator is a callable that takes a function and returns a modified version. @functools.wraps preserves the original function's name and docstring. Built-in decorators: @property (computed attribute), @staticmethod (no self/cls), @classmethod (receives cls), @functools.cache (memoisation), @functools.lru_cache(maxsize=128). Decorator factories take arguments: @lru_cache(maxsize=256). Class decorators: @dataclass generates __init__, __repr__, __eq__ from class annotations — Python's equivalent of PHP 8.1 readonly classes. Stacking: @app.route('/') @login_required applies inside-out. PHP's equivalent is attribute-based annotations (#[Route]) or middleware patterns — Python decorators are more powerful because they execute arbitrary code, not just metadata.

Diagram

flowchart LR
    subgraph Decorator_Mechanics
        ORIG[def my_func]
        WRAP[def decorator wraps func]
        APPLIED[my_func = decorator my_func]
        ORIG --> WRAP --> APPLIED
    end
    subgraph Common_Uses
        TIMING[measure_time decorator<br/>log execution duration]
        CACHE2[functools.lru_cache<br/>memoize results]
        AUTH2[require_auth decorator<br/>check before running]
        RETRY2[retry decorator<br/>retry on exception]
    end
    subgraph Syntax
        AT[at symbol decorator<br/>above function definition<br/>syntactic sugar]
    end
style APPLIED fill:#238636,color:#fff
style CACHE2 fill:#1f6feb,color:#fff
style AUTH2 fill:#f85149,color:#fff

Common Misconception

✗ Python decorators modify the original function in place. Decorators replace the original function with a wrapper — without functools.wraps, the decorated function loses its __name__, __doc__, and signature. Always use @functools.wraps(func) inside decorators to preserve metadata.

Why It Matters

Python decorators wrap functions or classes to add behaviour — used for caching, logging, auth, retry, and timing without modifying the function's core logic.

Common Mistakes

  • Not using functools.wraps — the decorated function loses its __name__ and __doc__.
  • Decorators that do not handle the wrapped function's arguments correctly — use *args, **kwargs.
  • Stateful decorators without thread safety — mutable state in a decorator is shared across calls.
  • Stacking too many decorators — execution order is bottom-up for application and top-down for stacking.

Code Examples

✗ Vulnerable
# Decorator without functools.wraps — loses metadata:
def log_call(func):
    def wrapper(*args, **kwargs):
        print(f'Calling {func.__name__}')
        return func(*args, **kwargs)
    return wrapper  # wrapper.__name__ is 'wrapper', not 'original'

# Fixed:
import functools
def log_call(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs): ...
    return wrapper
✓ Fixed
import functools

def retry(times=3):
    def decorator(fn):
        @functools.wraps(fn)
        def wrapper(*args, **kwargs):
            for i in range(times):
                try: return fn(*args, **kwargs)
                except Exception:
                    if i == times - 1: raise
        return wrapper
    return decorator

@retry(times=5)
def fetch_data(): ...

Added 15 Mar 2026
Edited 22 Mar 2026
Views 167
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
2 pings M 1 ping T 4 pings W 1 ping T 0 pings F 0 pings S 1 ping S 0 pings M 2 pings T 1 ping W 0 pings T 1 ping F 0 pings S 0 pings S 0 pings M 0 pings T 1 ping W 0 pings T 0 pings F 1 ping S 0 pings S 0 pings M 0 pings T 1 ping W 1 ping T 0 pings F 0 pings S 0 pings S 2 pings M 0 pings T
No pings yet today
SEMrush 1 PetalBot 1
Amazonbot 15 PetalBot 13 Perplexity 9 Ahrefs 9 ChatGPT 9 SEMrush 9 Google 8 Bing 7 Scrapy 6 Claude 4 Twitter/X 4 Brave Search 4 Unknown AI 3 Meta AI 2 Applebot 2 Baidu 1
crawler 100 crawler_json 5
🧱 FUNDAMENTALS — new to this? Start with the ground floor.
Python general Python is a programming language known for readable syntax and versatility, used for web development, data science, automation, and more.

Python's gentle learning curve makes it an ideal first language, while its vast ecosystem keeps it relevant for machine learning, APIs, and DevOps. Skills transfer directly to professional environments because Python runs in production at companies of every size.

💡 When Python throws IndentationError, check that every block uses the same whitespace style—pick spaces (preferably 4) and stick with them everywhere.

Ask Codex about Python →
⚡ DEV INTEL Tools & Severity
🟢 Low ⚙ Fix effort: Medium
⚡ Quick Fix
Always use functools.wraps(func) inside your decorator to preserve the wrapped function's __name__, __doc__, and type hints
📦 Applies To
python 2.4 web cli
🔗 Prerequisites
🔍 Detection Hints
Decorator missing @functools.wraps(func) losing wrapped function metadata; repeated cross-cutting concerns not extracted as decorators
Auto-detectable: ✓ Yes pylint ruff mypy
⚠ Related Problems
🤖 AI Agent
Confidence: Low False Positives: High ✗ Manual fix Fix: Medium Context: Function Tests: Update


✓ schema.org compliant