Python decorators are a powerful feature that allows you to modify the behavior of functions or methods without changing their actual code. They are widely used in Python for logging, access control, instrumentation, caching, and more. Let’s dive into what decorators are, how they work, and see some practical examples.
What is a Decorator?
A decorator in Python is essentially a function that takes another function as an argument and extends or alters its behavior. It’s a form of metaprogramming, allowing you to add functionality to your functions or methods dynamically.
Basic Structure of a Decorator
Here’s a simple example to illustrate the concept:
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
Output:
Something is happening before the function is called.
Hello!
Something is happening after the function is called.
In this example:
my_decorator
is the decorator function.say_hello
is the function being decorated.wrapper
is an inner function that extends the behavior ofsay_hello
.
How Decorators Work
- Define a Decorator Function: This function takes another function as its argument.
- Create a Wrapper Function: This function wraps the original function and can execute code before or after the original function.
- Return the Wrapper Function: The decorator function returns the wrapper function.
Using Decorators with Arguments
You can also create decorators that accept arguments. Here’s an example:
def repeat(num_times):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
func(*args, **kwargs)
return wrapper
return decorator
@repeat(num_times=3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Output:
Hello, Alice!
Hello, Alice!
Hello, Alice!
In this example:
repeat
is a decorator factory that takes an argument.decorator
is the actual decorator that wraps the original function.wrapper
is the function that is called multiple times.
Common Uses of Decorators
- Logging: Track function calls and their results.
- Authorization: Check if a user has permission to access a resource.
- Caching: Store results of expensive function calls to improve performance.
Example: Logging Decorator
Here’s an example of a decorator that logs function execution details:
import functools
def log_function_call(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with arguments {args} and keyword arguments {kwargs}")
result = func(*args, **kwargs)
print(f"{func.__name__} returned {result}")
return result
return wrapper
@log_function_call
def add(x, y):
return x + y
add(5, 7)
Output:
Calling add with arguments (5, 7) and keyword arguments {}
add returned 12
In this example, the log_function_call
decorator logs the function’s arguments and return value.
Conclusion
Python decorators are a versatile feature that can help you extend or modify the behavior of your functions in a clean and maintainable way. By understanding how decorators work and how to implement them, you can leverage their power to write more modular and reusable code.
Feel free to experiment with decorators and see how they can simplify and enhance your Python code. Happy coding!