Python decorators are a powerful and flexible tool that allows you to modify or extend the behavior of functions or methods without permanently changing their code. They are widely used in frameworks, libraries, and everyday coding tasks for enhancing functionality. In this article, we’ll explore decorators in detail, complete with examples and practical applications.

What Are Decorators in Python?
A decorator in Python is a function that takes another function (or method) as input, modifies or extends its behavior, and returns the modified function. Decorators provide a clean and concise way to dynamically alter functionality without modifying the original code.
The Basics of Decorators in Python
To create a decorator, you need:
- A Wrapper Function:
This function defines the additional behavior. - @ Syntax:
A syntactic sugar to apply decorators to functions or methods.
Here’s a simple example:
def decorator_function(original_function): def wrapper_function(): print(f"Before calling {original_function.__name__}") original_function() print(f"After calling {original_function.__name__}") return wrapper_function @decorator_function def greet(): print("Hello, World!") greet()
OUTPUT:
Before calling greet
Hello, World!
After calling greet
How Decorators in Python Work
- Define the Decorator:
- A decorator function takes a function as an argument and returns a new function.
- Apply the Decorator:
- Use the
@decorator_name
syntax before the function definition.
- Use the
- Modify Behavior:
- The wrapper function can add pre- or post-execution logic.
Key Applications of Decorators in Python
1.Logging:
Track function calls and their arguments for debugging or monitoring.
def log_decorator(func): def wrapper(*args, **kwargs): print(f"Function {func.__name__} called with arguments {args} and {kwargs}") return func(*args, **kwargs) return wrapper @log_decorator def add(a, b): return a + b print(add(3, 5))
2.Access Control:
Restrict access to sensitive functions based on user roles.
def admin_required(func): def wrapper(user): if user != "admin": print("Access denied") return return func(user) return wrapper @admin_required def view_admin_dashboard(user): print(f"Welcome, {user}! Here's the admin dashboard.") view_admin_dashboard("guest") # Output: Access denied view_admin_dashboard("admin") # Output: Welcome, admin! Here's the admin dashboard.
3.Performance Measurement:
Calculate the execution time of a function.
import time def timer_decorator(func): def wrapper(*args, **kwargs): start_time = time.time() result = func(*args, **kwargs) end_time = time.time() print(f"{func.__name__} executed in {end_time - start_time:.4f} seconds") return result return wrapper @timer_decorator def slow_function(): time.sleep(2) print("Finished!") slow_function()
Chaining Multiple Decorators
You can apply multiple decorators to a single function. The decorators are applied in a bottom-up order.
def uppercase_decorator(func): def wrapper(): return func().upper() return wrapper def exclamation_decorator(func): def wrapper(): return func() + "!!!" return wrapper @exclamation_decorator @uppercase_decorator def greet(): return "hello" print(greet()) # Output: HELLO!!!
Built-in Decorators in Python
Python provides several built-in decorators:
- @static method: Defines a static method in a class.
- @class method: Defines a method bound to the class, not an instance.
- @property: Allows defining getter/setter methods for attributes.
Example:
class Circle: def __init__(self, radius): self._radius = radius @property def radius(self): return self._radius @radius.setter def radius(self, value): if value < 0: raise ValueError("Radius cannot be negative") self._radius = value
Advanced Concepts: Decorators with Arguments
Decorators can also accept arguments, adding more flexibility.
def repeat(times): def decorator(func): def wrapper(*args, **kwargs): for _ in range(times): func(*args, **kwargs) return wrapper return decorator @repeat(3) def greet(): print("Hello!") greet()
Real-World Use Cases of Decorators in Python
- Web Frameworks:
- Frameworks like Flask and Django use decorators for routing (
@app.route
) and middle ware.
- Frameworks like Flask and Django use decorators for routing (
- Event Handling:
- Use decorators for event binding in GUI or asynchronous programming.
- Testing Frameworks:
- Testing tools like Pytest use decorators for marking test cases (
@pytest.mark
).
- Testing tools like Pytest use decorators for marking test cases (
Advantages of Decorators in Python
- Modularization cross-cutting concerns like logging, caching, or validation.
- Reduce code duplication by isolating reusable functionality.
- Enhance code readability and maintainability.
Common Pitfalls and Best Practices
1.Preserving Metadata:
Use functools.wraps
to retain the original function’s metadata.
from functools import wraps def decorator(func): @wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) return wrapper
2.Avoid Over complication:
Keep decorators simple and focused on a single responsibility.
Conclusion
Python decorators are an essential tool for writing clean, efficient, and reusable code. Whether you’re implementing logging, access control, or routing in web applications, decorators provide a structured and elegant way to extend functionality.
Mastering decorators will not only improve your Python skills but also help you understand advanced programming concepts used in real-world software development.
INTERVIEW QUESTIONS
1.How would you write a doc-string for Decorators in Python? Provide an example.
Company:Google
- Answer: A decorator’s doc-string should describe its purpose, what it wraps, and any changes it makes to the function behavior. Example:
def log_function_call(func): """ A decorator that logs the name of the function and its arguments before calling it. Parameters: func (function): The function to be decorated. Returns: function: A wrapped function that logs the function name and arguments. """ def wrapper(*args, **kwargs): print(f"Calling {func.__name__} with arguments {args} and keyword arguments {kwargs}") return func(*args, **kwargs) return wrapper
2.What should the doc-string of a decorator include when the decorator takes arguments?
Company:TCS
- Answer: The doc-string should describe both the decorator’s arguments and its functionality. Example:
def repeat(times): """ A decorator that repeats the decorated function a specified number of times. Parameters: times (int): The number of times the function should be repeated. Returns: function: A wrapped function that calls the original function 'times' times. """ def decorator(func): def wrapper(*args, **kwargs): for _ in range(times): func(*args, **kwargs) return wrapper return decorator
3.How do you document the purpose of the wrapper function inside a decorator?
Company:Google
- Answer: The wrapper function’s doc-string should explain what the wrapper does and how it modifies the behavior of the original function.
- Example:
def timer(func): """ A decorator that measures and prints the execution time of the function. Parameters: func (function): The function to be decorated. Returns: function: A wrapped function that prints the execution time. """ import time def wrapper(*args, **kwargs): start_time = time.time() result = func(*args, **kwargs) end_time = time.time() print(f"{func.__name__} took {end_time - start_time} seconds to execute.") return result return wrapper
4.Why is it important to document a decorator’s parameters and return values in its doc-string?
Company:TCS
- Answer: It is important because:
- Documenting parameters clarifies how the decorator should be applied and what values it expects.
- Documenting the return value helps users understand what the decorator modifies or returns.
- It aids other developers in using the decorator correctly and knowing what to expect from it.
- It ensures maintainability, especially when multiple decorators are involved.
5.How would you document a decorator that adds additional functionality to the decorated function, like logging or access control?
Company:Amazon
- Answer: The doc-string should clearly describe the added functionality, such as logging or permission checking, and any specific details related to its behavior.
- Example:
def require_permission(permission): """ A decorator that checks if the user has the required permission before executing the function. Parameters: permission (str): The permission required to run the function. Returns: function: A wrapped function that checks permissions before execution. Raises: PermissionError: If the user does not have the required permission. """ def decorator(func): def wrapper(user, *args, **kwargs): if permission not in user.permissions: raise PermissionError(f"User does not have {permission} permission.") return func(user, *args, **kwargs) return wrapper return decorator
QUIZZES
Decorators in python Quiz
Question
Your answer:
Correct answer:
Your Answers