In Python, decorators are a powerful and elegant way to modify the behavior of functions or methods. They allow you to wrap a function or method with additional functionality without altering its core logic. Decorators are commonly used for logging, authentication, performance measurement, and many other tasks, making them an essential tool in a Python developer's toolkit.
In this blog post, we’ll break down what Python decorators are, how they work, and provide practical examples to help you understand how to use them effectively.
A decorator in Python is a design pattern that allows you to add functionality to an existing function or method. It is typically used to modify or enhance a function’s behavior without changing the function itself. In Python, decorators are implemented as functions that take another function as an argument and return a new function that usually extends or alters the behavior of the original function.
Decorators are often used to modify or extend the behavior of functions or methods in a reusable and readable manner. The most common use of decorators is in adding functionality like logging, validation, or access control to functions or methods in a clean and maintainable way.
Here’s a basic example to illustrate how decorators work:
def decorator_function(original_function):
def wrapper_function():
print("Wrapper executed this before {}".format(original_function.__name__))
return original_function()
return wrapper_function
@decorator_function
def display():
print("Display function executed!")
display()
Output:
Wrapper executed this before display
Display function executed!
In this example:
decorator_function
takes a function (original_function
) as an argument.wrapper_function
wraps the original_function
and adds behavior (in this case, a print statement) before calling it.@decorator_function
syntax is a shorthand for applying the decorator to the display
function, which means display
is passed to decorator_function
.Python decorators are implemented using higher-order functions. A higher-order function is a function that takes another function as an argument or returns a function as its result. Decorators are a specific type of higher-order function that are used to modify or enhance other functions.
@decorator
syntax.Creating your own decorators in Python is straightforward. A decorator is essentially a function that returns another function. Here’s a simple step-by-step example of creating a custom decorator.
def log_function_call(func):
def wrapper(*args, **kwargs):
print(f"Function {func.__name__} is called with arguments {args} and keyword arguments {kwargs}")
result = func(*args, **kwargs)
return result
return wrapper
@log_function_call
def add(a, b):
return a + b
add(2, 3)
Output:
Function add is called with arguments (2, 3) and keyword arguments {}
In this example:
log_function_call
decorator prints the function name, arguments, and keyword arguments whenever the decorated function is called.@log_function_call
is applied to the add
function, which logs the function call details every time it is invoked.Python provides several built-in decorators that can be used to add common functionality to your functions or methods. Some of the most commonly used built-in decorators include:
@staticmethod: Used to define a static method in a class, meaning it doesn’t depend on class or instance state.
class MyClass:
@staticmethod
def greet(name):
print(f"Hello, {name}!")
MyClass.greet("John")
class MyClass:
@classmethod
def greet(cls, name):
print(f"Hello from class, {name}!")
MyClass.greet("John")
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
c = Circle(5)
print(c.radius)
You can apply multiple decorators to a function, which allows you to add several layers of functionality. The decorators are applied in the order from bottom to top.
def decorator_one(func):
def wrapper():
print("Decorator One")
return func()
return wrapper
def decorator_two(func):
def wrapper():
print("Decorator Two")
return func()
return wrapper
@decorator_one
@decorator_two
def greet():
print("Hello!")
greet()
Output:
Decorator One
Decorator Two
Hello!
In this example:
greet()
is decorated with decorator_two
first and then with decorator_one
.greet()
is called, the decorators are executed in reverse order.Sometimes, you may need to pass arguments to a decorator. This requires an extra level of function nesting. Here’s how you can create a decorator that accepts arguments.
def repeat_decorator(repeats):
def decorator(func):
def wrapper(*args, **kwargs):
for _ in range(repeats):
func(*args, **kwargs)
return wrapper
return decorator
@repeat_decorator(3)
def say_hello():
print("Hello!")
say_hello()
Output:
Hello!
Hello!
Hello!
In this example:
repeat_decorator
takes an argument (repeats
) and repeats the decorated function's execution that many times.say_hello()
, it prints "Hello!" three times.Python decorators are used in a wide range of real-world scenarios. Some common use cases include: