Hello! I'm Labby, and I'm happy to help you understand why decorators are so useful in Python!
In the current step, you've learned how to pass arguments through a decorator. While the example uses print("Before func!"), real-world applications are much more powerful.
Here are some of the most common use cases for decorators:
1. Logging and Debugging
Decorators are perfect for logging when a function is called, what arguments it received, and what it returned, without cluttering the business logic.
def logger(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with {args}")
return func(*args, **kwargs)
return wrapper
2. Authorization and Authentication
In web frameworks (like Flask or Django), decorators are used to check if a user is logged in or has specific permissions before allowing them to access a page.
@login_required
def view_profile():
# Only runs if user is logged in
pass
3. Timing and Performance
You can measure how long a function takes to execute. This is very helpful for optimizing slow parts of your code.
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
print(f"Executed in {time.time() - start} seconds")
return result
return wrapper
4. Input Validation
You can use decorators to verify that the arguments passed to a function are valid (e.g., checking if an age is a positive integer) before the function even starts running.
5. Caching (Memoization)
Decorators can store the results of expensive function calls and return the cached result when the same inputs occur again. Python even has a built-in one: @functools.lru_cache.
Summary
The core idea is "separation of concerns." The decorator handles the "extra" tasks (logging, timing, security), while the function focuses solely on its main job.
Try finishing the current step to see how *args and **kwargs make your decorators flexible enough to handle any of these scenarios! Happy learning