How to Write Your First Python Decorator: A Beginner’s Guide
Introduction: The "Magic" Behind Decorators
Imagine you’re hosting a party. Before guests arrive, you set up the lights, music, and snacks. After they leave, you clean up. What if you could automate this "pre-party" and "post-party" routine every time?
In Python, decorators work similarly—they let you add extra functionality to a function without changing its core code. Whether it’s logging, timing, or access control, decorators help you keep your code clean and reusable.
The best part? Writing your first decorator is easier than you think! Let’s break it down step by step.
What Is a Python Decorator?
A decorator is a function that takes another function and extends its behavior without modifying it directly. Think of it like a wrapper:
def my_decorator(func):
def wrapper():
print("Something happens before the function.")
func() # Call the original function
print("Something happens after the function.")
return wrapper
When you apply @my_decorator to a function, Python automatically "wraps" it:
@my_decorator
def say_hello():
print("Hello!")
say_hello()
Output:
Something happens before the function.
Hello!
Something happens after the function.
Writing Your First Decorator: A Step-by-Step Example
Let’s build a simple decorator that logs when a function starts and finishes.
Step 1: Define the Decorator
def logger(func):
def wrapper():
print(f"Starting {func.__name__}...")
func() # Run the original function
print(f"Finished {func.__name__}!")
return wrapper
Step 2: Apply It to a Function
@logger
def greet():
print("Welcome to Python decorators!")
greet()
Output:
Starting greet...
Welcome to Python decorators!
Finished greet!
How It Works:
loggertakesgreetas input.wrapper()adds behavior before/after callinggreet().@loggerapplies the decorator automatically.
Leveling Up: Decorators with Arguments
What if your function has arguments? Use *args and **kwargs:
def logger(func):
def wrapper(*args, **kwargs):
print(f"Starting {func.__name__}...")
result = func(*args, **kwargs) # Pass arguments to the original function
print(f"Finished {func.__name__}!")
return result # Return the original function's result
return wrapper
@logger
def add(a, b):
return a + b
print(add(2, 3))
Output:
Starting add...
Finished add!
5
Now your decorator works with any function, regardless of arguments!
Real-World Use Cases for Decorators
Decorators are everywhere in Python. Here are a few practical examples:
Timing Functions
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:.2f} seconds.") return result return wrapper @timer def slow_function(): time.sleep(2) slow_function() # Output: "slow_function took 2.00 seconds."Authorization Checks
def admin_required(func): def wrapper(user, *args, **kwargs): if user != "admin": raise PermissionError("Admins only!") return func(user, *args, **kwargs) return wrapper @admin_required def delete_database(user): print("Database deleted!") delete_database("admin") # Works delete_database("guest") # Raises PermissionErrorCaching (Memoization)
def cache(func): stored = {} def wrapper(*args): if args in stored: return stored[args] result = func(*args) stored[args] = result return result return wrapper @cache def expensive_calculation(n): print(f"Calculating {n}...") return n * n print(expensive_calculation(4)) # Prints "Calculating 4..." print(expensive_calculation(4)) # Returns cached result (no print)
Conclusion: Your Decorator Journey Starts Now!
Decorators are like superpowers for your functions—they let you add reusable features without cluttering your code. You’ve just learned:
✅ How to write a basic decorator
✅ Handling functions with arguments (*args, **kwargs)
✅ Real-world applications (logging, timing, caching)
Now it’s your turn! What’s the first decorator you’ll build?
- A retry decorator for failed requests?
- A rate limiter for API calls?
- A debug decorator to print inputs/outputs?
Drop your ideas below! 👇 #CodingFun
(Hint: Try combining multiple decorators—like @timer @logger—and see what happens!) 🚀