Introduction
A Decorator is a powerful Python feature that allows you to modify or extend the behavior of a function without changing its original code.
Decorators are commonly used for:
-
Logging
-
Authentication
-
Authorization
-
Performance Monitoring
-
Exception Handling
-
Timing Function Execution
-
Automation Framework Development
Decorators follow the principle of “wrapping” a function inside another function.
In this tutorial, you will learn what decorators are, how they work, practical examples, automation testing use cases, common mistakes, and best practices.
What is a Decorator?
A decorator is a function that takes another function as an argument, adds some functionality, and returns a new function.
Basic Syntax
def decorator_function(original_function):
def wrapper():
print("Before function execution")
original_function()
print("After function execution")
return wrapper
Why Use Decorators?
Decorators help:
-
Reuse code
-
Avoid code duplication
-
Add functionality dynamically
-
Improve code readability
-
Follow the DRY (Don’t Repeat Yourself) principle
Functions are First-Class Objects
In Python, functions can be:
-
Assigned to variables
-
Passed as arguments
-
Returned from other functions
Example
def greet():
print("Hello")
message = greet
message()
Output
Hello
This concept makes decorators possible.
Creating a Simple Decorator
Example
def my_decorator(func):
def wrapper():
print("Before execution")
func()
print("After execution")
return wrapper
def display():
print("Display Function")
decorated_function = my_decorator(display)
decorated_function()
Output
Before execution
Display Function
After execution
Using @ Decorator Syntax
Python provides a cleaner syntax.
Example
def my_decorator(func):
def wrapper():
print("Before execution")
func()
print("After execution")
return wrapper
@my_decorator
def display():
print("Display Function")
display()
Output
Before execution
Display Function
After execution
This is equivalent to:
display = my_decorator(display)
Decorator with Function Arguments
Example
def decorator(func):
def wrapper(name):
print("Welcome")
func(name)
print("Thank You")
return wrapper
@decorator
def greet(name):
print(f"Hello {name}")
greet("John")
Output
Welcome
Hello John
Thank You
Decorator Using *args and **kwargs
This makes decorators work with any number of arguments.
Example
def decorator(func):
def wrapper(*args, **kwargs):
print("Function Started")
func(*args, **kwargs)
print("Function Ended")
return wrapper
@decorator
def add(a, b):
print(a + b)
add(10, 20)
Output
30
Function Ended
Actually output:
Function Started
30
Function Ended
Decorator Returning Values
Example
def decorator(func):
def wrapper(*args, **kwargs):
result = func(*args, **kwargs)
return result
return wrapper
@decorator
def multiply(a, b):
return a * b
print(multiply(5, 4))
Output
20
Logging Decorator
Example
def logger(func):
def wrapper(*args, **kwargs):
print(
f"Executing {func.__name__}"
)
return func(*args, **kwargs)
return wrapper
@logger
def login():
print("User Logged In")
login()
Output
Executing login
User Logged In
Execution Time Decorator
Example
import time
def timer(func):
def wrapper(*args, **kwargs):
start = time.time()
result = func(*args, **kwargs)
end = time.time()
print(
f"Execution Time: {end-start}"
)
return result
return wrapper
@timer
def process():
time.sleep(2)
process()
Output
Execution Time: 2.00...
Authentication Decorator
Example
def authenticate(func):
def wrapper(user):
if user == "admin":
return func(user)
print("Access Denied")
return wrapper
@authenticate
def dashboard(user):
print("Welcome Admin")
dashboard("admin")
Output
Welcome Admin
Multiple Decorators
Example
def uppercase(func):
def wrapper():
return func().upper()
return wrapper
def add_stars(func):
def wrapper():
return f"*** {func()} ***"
return wrapper
@add_stars
@uppercase
def message():
return "hello"
print(message())
Output
*** HELLO ***
Preserving Function Metadata
Without precautions, decorators can change a function’s name and documentation.
Use functools.wraps().
Example
from functools import wraps
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
Practical Example: Exception Handling Decorator
Example
def handle_exception(func):
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)
except Exception as e:
print(f"Error: {e}")
return wrapper
@handle_exception
def divide(a, b):
return a / b
print(divide(10, 0))
Output
Error: division by zero
None
Decorators in Selenium Automation
Example
Capture test execution logs.
def log_test(func):
def wrapper():
print(
f"Running {func.__name__}"
)
func()
print(
f"Completed {func.__name__}"
)
return wrapper
@log_test
def login_test():
print("Executing Login Test")
login_test()
Output
Running login_test
Executing Login Test
Completed login_test
Decorators in API Automation
Example
Log API requests.
def api_logger(func):
def wrapper(*args, **kwargs):
print("Sending API Request")
response = func(*args, **kwargs)
print("Response Received")
return response
return wrapper
@api_logger
def get_users():
return {"status": 200}
print(get_users())
Output
Sending API Request
Response Received
{'status': 200}
Common Mistakes Beginners Make
Forgetting to Return Wrapper
Incorrect
def decorator(func):
def wrapper():
pass
Decorator won’t work properly.
Correct
def decorator(func):
def wrapper():
pass
return wrapper
Forgetting *args and **kwargs
Incorrect
def wrapper():
May fail for functions with parameters.
Correct
def wrapper(*args, **kwargs):
Not Returning Function Result
Incorrect
func(*args, **kwargs)
Result is lost.
Correct
return func(*args, **kwargs)
Forgetting @wraps
Without it:
print(func.__name__)
May show:
wrapper
instead of the original function name.
Best Practices
Use functools.wraps()
from functools import wraps
Keep Decorators Focused
One decorator should perform one task.
Examples:
-
Logging
-
Authentication
-
Timing
Use *args and **kwargs
Ensures flexibility.
Return Results Properly
return result
Avoid Excessive Nesting
Too many decorators can make debugging difficult.
Advantages of Decorators
-
Reusable code
-
Cleaner functions
-
Better maintainability
-
Reduced duplication
-
Powerful customization
Limitations of Decorators
-
Can be difficult for beginners
-
Excessive nesting may reduce readability
-
Debugging can become complex
Decorator vs Normal Function
| Feature | Decorator | Normal Function |
|---|---|---|
| Modifies Other Functions | Yes | No |
| Reusable | High | Moderate |
| Supports Wrapping | Yes | No |
| Code Duplication Reduction | Yes | No |
Conclusion
Function decorators are one of Python’s most elegant features for extending function behavior without modifying existing code. They help keep code clean, reusable, and maintainable.
Whether you’re adding logging, timing, authentication, exception handling, or automation framework utilities, decorators provide a powerful way to apply functionality consistently across multiple functions.
Mastering decorators is an essential skill for intermediate and advanced Python developers, especially those building Selenium, API, and automation frameworks.
Frequently Asked Questions (FAQs)
What is a decorator in Python?
A decorator is a function that modifies the behavior of another function.
What does @decorator_name mean?
It is shorthand for:
function = decorator_name(function)
Why use *args and **kwargs in decorators?
They allow decorators to work with functions having any number of arguments.
What is functools.wraps() used for?
It preserves the original function’s metadata such as name and docstring.
Can multiple decorators be applied?
Yes.
@decorator1
@decorator2
def my_function():
pass
Key Takeaways
-
Decorators modify function behavior without changing original code.
-
Decorators are functions that accept other functions as arguments.
-
The
@syntax provides a clean way to apply decorators. -
*argsand**kwargsmake decorators flexible. -
functools.wraps()preserves metadata. -
Common uses include logging, timing, authentication, and exception handling.
-
Decorators reduce code duplication.
-
Widely used in Selenium and API automation frameworks.
-
Multiple decorators can be stacked.
-
Decorators are an important feature for writing clean and maintainable Python code.
