📌 Quick Info
- Topic: Decorators in Python
- Target Audience: Beginners who know functions and want to level up
- Goal: Demystify one of Python's most misunderstood features
1. Introduction
"Decorators looked like magic the first time I saw them. The
@symbol, a function wrapping another function... none of it made sense. Then I learned they're just functions calling functions. Here's how they actually work."
2. The Problem (Repeating Code)
def greet():
print("Starting...")
print("Hello!")
print("Finishing...")
def farewell():
print("Starting...")
print("Goodbye!")
print("Finishing...")
Problem: The “Starting….” and “Finishing….” lines are repeated. If you want to change them, you edit every function.
3. The Solution (Decorators)
def log_before_and_after(func):
def wrapper():
print("Starting...")
func()
print("Finishing...")
return wrapper
@log_before_and_after
def greet():
print("Hello!")
@log_before_and_after
def farewell():
print("Goodbye!")
greet()
Output:
Starting...
Hello!
Finishing...
The @ symbol applies the decorator to the function- now greet automatically logs before and after.
4. How It Works (Step by Step)
- Step 1: log_before_and_after takes a function as input.
- Step 2: Inside, it defines wraper()- a new function that runs extra code before and after calling func().
- Step 3: It returns wrapper.
- Step 4: @log_before_and_after above greet is the same as writing greet= log_before_and_after(greet).
That’s the whole trick. Decorators are just functions that take functions and return functions.
5. Real Example (Timing)
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(f"{func.__name__} took {end - start:.4f}s")
return result
return wrapper
@timer
def slow_function():
time.sleep(1)
return "Done"
print(slow_function())
Output:
slow_function took 1.0023s
Done
Now you can time any function by adding @timer above it. No changes to the function itself.
6. Why args* and **kwargs?
Notice wrapper(*args, **kwargs) in the example above. That's what makes the decorator work with any function — no matter how many arguments it takes. Without it, the decorator only works on functions with no arguments.
7. Common Use Cases
| Use Case | What It Does |
|---|---|
| Logging | Print when a function starts and ends |
| Timing | Measure how long a function takes |
| Catching | Store Results so you don’t recompute |
| Authentication | Check permissions before running a function. |
| Retry Logic | Try again if the function fails |
8. What I learned
- A decorator is a function that wraps another function.
- @decorator is shorthand for func= decorator (func).
- **args, *kwargs makes a decorator work with any function.
- Decorators are everywhere in real code- flask, Django, pytest all use them.
- Start simple- one decorator that logs is enough to understand the pattern.
9. Conclusion
"Decorators looked like wizardry until I understood they're just functions calling functions. Now I use them for logging and timing everywhere. The @ symbol isn't magic — it's a shortcut.”
Top comments (0)