π§© Python Decorators β Wrap, Enhance, and Reuse Functions
π§² Introduction β What Are Python Decorators?
In Python, decorators are a powerful feature that lets you modify or extend the behavior of a function or classβwithout changing its actual code.
They are commonly used for logging, authentication, timing functions, and more. Python decorators leverage closures and higher-order functions, making them a go-to tool for writing cleaner, DRY (Don’t Repeat Yourself) code.
π― In this guide, youβll learn:
- What decorators are and how they work
- How to define and apply decorators
- The role of @syntax andfunctools.wraps
- Real-world examples and best practices
π§ What Is a Decorator?
A decorator is a function that takes another function as input and returns a new function that usually enhances or wraps the original.
def decorator(func):
    def wrapper():
        print("Before function runs")
        func()
        print("After function runs")
    return wrapper
β Applying a Decorator
@decorator
def say_hello():
    print("Hello!")
say_hello()
π§ Output:
Before function runs  
Hello!  
After function runs
π Explanation:
The @decorator is shorthand for say_hello = decorator(say_hello).
π¦ Real-World Decorator Example: Logger
def logger(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with {args} and {kwargs}")
        return func(*args, **kwargs)
    return wrapper
@logger
def greet(name):
    return f"Hello, {name}"
print(greet("Alice"))
π§  Using *args and **kwargs in Decorators
Use *args and **kwargs to accept functions with any number of arguments, keeping your decorators reusable and flexible.
βοΈ functools.wraps β Preserve Metadata
from functools import wraps
def smart_decorator(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("Decorated function running...")
        return func(*args, **kwargs)
    return wrapper
β
 Always use @wraps(func) inside decorators to preserve:
- Function name
- Docstring
- Signature
π Chaining Multiple Decorators
@decorator1  
@decorator2  
def my_func():
    pass
Python applies them bottom-up, so decorator2 wraps my_func, and then decorator1 wraps the result.
π§ͺ Built-in Decorators You Should Know
| Decorator | Purpose | 
|---|---|
| @staticmethod | Define a method without self | 
| @classmethod | Takes clsinstead ofself | 
| @property | Make method behave like an attribute | 
β οΈ Common Pitfalls
| Mistake | Fix | 
|---|---|
| Not returning wrapper() | Always return the nested function | 
| Forgetting @wraps | Use @wraps(func)to retain metadata | 
| Not using *args, **kwargs | Needed for flexibility in decorators | 
π‘ Best Practices
- β
 Always use functools.wrapsinside decorators
- β Use decorators for cross-cutting concerns like logging, validation, caching
- β Keep decorators small and focused
- β οΈ Avoid complex logic inside decoratorsβprefer clarity over cleverness
π Summary β Recap & Next Steps
Python decorators are a clean and elegant way to enhance or modify function behavior without changing their code. By using closures and higher-order functions, decorators help build flexible, maintainable, and reusable components.
π Key Takeaways:
- β
 Decorators wrap functions using the @decoratorsyntax.
- β
 *argsand**kwargsallow decorators to work with any function signature.
- β
 Use functools.wrapsto retain original function metadata.
- β Great for logging, timing, authentication, and more.
βοΈ Real-World Relevance:
Decorators are widely used in Flask/Django, unit testing, and API rate-limiting, allowing clean and modular code enhancements across production-grade applications.
β FAQ Section β Python Decorators
β What is a decorator in Python?
β A decorator is a function that wraps another function to extend or modify its behavior without altering the function’s code directly.
β How do I create a custom decorator?
β Define a function that takes another function as an argument and returns a wrapper function:
def decorator(func):
    def wrapper():
        return func()
    return wrapper
β Why use *args and **kwargs in decorators?
β To ensure your decorator works with functions of any number of positional and keyword arguments.
β What does @functools.wraps do?
β It preserves the metadata (like name, docstring, signature) of the original function, making debugging and introspection easier.
β Can I use multiple decorators on one function?
β Yes. Decorators are stacked from bottom to top and applied in that order:
@first
@second
def func():
    pass
Share Now :
