What Are Python Decorators?
In Python, decorators are a powerful tool that allows you to modify or extend the behavior of functions or methods without changing their actual code. They are essentially functions that wrap other functions or methods, adding additional functionality.
Understanding Python Decorators with Real-World Examples
How Do Decorators Work?
Decorators in Python are a way to modify or extend the behavior of functions or methods. Here’s a brief overview of how they work:
Functions as Objects: In Python, functions are first-class objects. This means they can be passed as arguments, returned from other functions, and assigned to variables.
Creating a Decorator: A decorator is essentially a function that takes another function as an argument. Inside the decorator, you define a new function (often called wrapper) that adds or alters the behavior of the original function. The decorator then returns this wrapper function.
Applying a Decorator: You apply a decorator to a function using the @decorator_name syntax just above the function definition. This syntax is shorthand for passing the function to the decorator and reassigning it to the original function name.
Basic Example
Here's a simple example of a decorator that prints a message before and after the execution of a function:
def my_decorator(func):
def wrapper():
print("Something is happening before the function.")
func()
print("Something is happening after the function.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
Output:
Something is happening before the function.
Hello!
Something is happening after the function.
Real-World Examples
1. Logging Decorator
A common use of decorators is to add logging to functions. Here’s an example of a logging decorator:
import logging
logging.basicConfig(level=logging.INFO)
def log_decorator(func):
def wrapper(*args, **kwargs):
logging.info(f"Function '{func.__name__}' was called")
result = func(*args, **kwargs)
logging.info(f"Function '{func.__name__}' returned {result}")
return result
return wrapper
@log_decorator
def add(a, b):
return a + b
add(5, 3)
Output:
INFO:root:Function 'add' was called
INFO:root:Function 'add' returned 8
2. Timing Decorator
Another useful decorator is one that measures the execution time of a function:
import time
def timeit_decorator(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"Function '{func.__name__}' took {end_time - start_time} seconds to execute")
return result
return wrapper
@timeit_decorator
def slow_function():
time.sleep(2)
return "Done!"
slow_function()
Output:
Function 'slow_function' took 2.002345 seconds to execute
Advanced Decorators
1. Decorators with Arguments
You can also create decorators that accept arguments:
def repeat_decorator(times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat_decorator(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Output:
Hello, Alice!
Hello, Alice!
Hello, Alice!
2. Class-Based Decorators
Decorators can also be implemented as classes:
class MyDecorator:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print("Before function call")
result = self.func(*args, **kwargs)
print("After function call")
return result
@MyDecorator
def say_hello():
print("Hello!")
say_hello()
Output:
Before function call
Hello!
After function call
Conclusion
Python decorators are a versatile and powerful feature that can be used to enhance the functionality of functions and methods in a clean and reusable way. By understanding and utilizing decorators, you can write more modular, readable, and maintainable code.