Decorators
Decorators: Enhance Functions Without Touching Their Source
What might confuse you right now
"Why not just edit the function body directly? Why wrap it?"
When you need to add the same capability (logging, auth, timing) to many functions, decorators eliminate duplicate code and enforce consistency.
One-line definition
A decorator is a higher-order function that takes a function and returns an enhanced version.
Real-life analogy
Like putting a case and screen protector on your phone: the phone itself doesn't change, but it gains new protection.
Minimal runnable example
from functools import wraps
def logger(func):
@wraps(func)
def wrapper(*args, **kwargs):
print(f"[LOG] calling: {func.__name__}")
return func(*args, **kwargs)
return wrapper
@logger
def greet(name):
return f"Hello, {name}"
print(greet("JR"))
Key concepts
@loggeris equivalent togreet = logger(greet)wrapperhandles "enhanced behavior + calling the original function"@wrapspreserves the original function's metadata (name, docstring)
Quick quiz (5 min)
- Write a decorator that prints the function name.
- Modify it to also print the arguments.
- Implement
@timerthat outputs execution time in ms.
Quiz answer guidelines & grading criteria
- Answer direction: working code that covers core conditions and edge inputs from the prompt.
- Criterion 1 (Correctness): Main flow produces correct results, key branches execute.
- Criterion 2 (Readability): Clear variable names, no excessive nesting.
- Criterion 3 (Robustness): Basic protection against null values, type errors, or unexpected input.
Transfer task (homework)
Take two functions that both have duplicate logging code and refactor them into "one decorator + two pure business functions."
Acceptance criteria
You can independently:
- Write a basic decorator
- Properly handle
*args, **kwargs - Explain how decorators replace the original function call chain
Common errors & debugging steps (beginner edition)
- Can't understand the error: read the last line for the error type (e.g.,
TypeError,NameError), then trace back to the relevant code line. - Not sure about a variable's value: temporarily add
print(variable, type(variable))at key points to verify data matches expectations. - Code changes aren't taking effect: confirm the file is saved, you're running the right file, and your terminal environment (venv) is correct.
Common misconceptions
-
Misconception: Forgetting
return wrapper. -
Reality: Without the return, the replacement never happens.
-
Misconception: Not writing
*args, **kwargs, which drops arguments. -
Reality: A generic decorator must pass through all arguments.