In Python, a decorator is a function that allows you to modify the behavior of another function or method. Decorators are commonly used to enhance the functionality of functions or methods without changing their code. They provide a simple and readable way to extend the functionality of functions in a modular way.
At its core, a decorator is a function that takes another function as an argument, adds some functionality, and then returns a new function that behaves in the same way as the original function but with added behavior.
The syntax for a decorator is very simple. Here's an example of how you would use a decorator in Python:
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()
In this example, the my_decorator
function takes func
as an argument (the function to be decorated). Inside the decorator, we define a nested function called wrapper
, which adds functionality before and after calling the original function (func
). The @my_decorator
syntax is shorthand for passing the say_hello
function to my_decorator
as an argument.
Something is happening before the function is called. Hello! Something is happening after the function is called.
Decorators are useful for several reasons:
One common use case for decorators is logging. You can create a decorator that logs the time when a function is called and when it finishes execution. Here's an example:
import time def log_function_call(func): def wrapper(): print(f"Function {func.__name__} called at {time.ctime()}") func() print(f"Function {func.__name__} finished at {time.ctime()}") return wrapper @log_function_call def process_data(): print("Processing data...") time.sleep(2) print("Data processed.") process_data()
In this example, the log_function_call
decorator logs the time when the process_data
function is called and when it finishes. The time.sleep(2)
is used to simulate a time-consuming process.
Function process_data called at Fri Nov 30 10:30:02 2024 Processing data... Data processed. Function process_data finished at Fri Nov 30 10:30:04 2024
Decorators can also accept arguments. This allows you to create more flexible decorators that can be customized based on input. Here's an example of a decorator that accepts an argument:
def repeat(num_times): def decorator(func): def wrapper(): for _ in range(num_times): func() return wrapper return decorator @repeat(3) def greet(): print("Hello!") greet()
In this example, the repeat
decorator takes an argument num_times
, which specifies how many times the decorated function should be called. The greet
function is called three times because of the decorator.
Hello! Hello! Hello!
You can apply multiple decorators to a single function by stacking them. The decorators are applied from bottom to top:
def decorator_one(func): def wrapper(): print("Decorator One") func() return wrapper def decorator_two(func): def wrapper(): print("Decorator Two") func() return wrapper @decorator_one @decorator_two def greet(): print("Hello!") greet()
In this example, greet
is decorated with both decorator_one
and decorator_two
. The output shows the decorators are applied in the order from bottom to top.
Decorator One Decorator Two Hello!
Decorators in Python are not limited to functions; they can also be used with classes. A class-based decorator works similarly to a function-based decorator but is implemented using a class. Here's an example:
class DecoratorClass: def __init__(self, func): self.func = func def __call__(self): print("Before function call") self.func() print("After function call") @DecoratorClass def greet(): print("Hello!") greet()
In this example, the DecoratorClass
acts as a decorator. The __call__
method allows the instance of the class to be used as a decorator, adding functionality before and after the function call.
Before function call Hello! After function call
Decorators are a powerful feature in Python that allows you to modify or enhance the behavior of functions or methods in a clean and modular way. By using decorators, you can add functionality such as logging, access control, and caching without modifying the original function. They are widely used in Python frameworks and libraries to simplify code and increase reusability.