Decorators are a powerful and convenient feature of the Python programming language. They allow you to extend and modify the behavior of existing functions and methods without modifying their code. In this post, we will explore the basics of decorators and demonstrate how to use them in your own Python code.
Table of contents
Open Table of contents
What are Decorators?
Decorators are a way to dynamically alter the behavior of a function or method. They are typically defined as functions that take another function as an argument and return a modified version of that function. This modified version is then called instead of the original function, allowing you to extend or modify the behavior of the original function without modifying its code.
Here is a simple example of a decorator function in Python:
def my_decorator(func):
def wrapper():
print('Before calling the function')
func()
print('After calling the function')
return wrapper
This my_decorator
function takes a function as an argument and returns a modified version of that function. The modified version, which is defined as the wrapper
function, first prints a message, then calls the original function, and then prints another message.
To use this decorator, you would apply it to a function using the @
symbol, like this:
@my_decorator
def my_function():
print('Inside the function')
This code defines a simple function named my_function
that prints a message. The @my_decorator
syntax applied to the function indicates that the my_decorator
decorator should be applied to the my_function
function. This means that whenever the my_function
function is called, the my_decorator
decorator will be applied to it, and the wrapper
function returned by the decorator will be called instead of the original my_function
function.
If you call the my_function
function, you will see the following output:
Before calling the function
Inside the function
After calling the function
This output shows the messages printed by the wrapper
function before and after calling the original my_function
function. This demonstrates how decorators can be used to extend and modify the behavior of existing functions without modifying their code.
Decorator Arguments
Decorator functions can also accept arguments. This allows you to customize the behavior of the decorator for each function it is applied to. Here is an example of a decorator function that accepts an argument:
def my_decorator(arg1, arg2):
def real_decorator(func):
def wrapper(*args, **kwargs):
print('Before calling the function')
print('Decorator arguments:', arg1, arg2)
func(*args, **kwargs)
print('After calling the function')
return wrapper
return real_decorator
This my_decorator
function accepts two arguments, arg1
and arg2
, and returns a decorator function. The decorator function accepts a function as an argument and returns a modified version of that function. The modified version, which is defined as the wrapper
function, first prints a message, then prints the values of the arg1
and arg2
arguments, then calls the original function, and then prints another message.
To use this decorator, you would apply it to a function using the @
symbol and specify the arguments for the decorator, like this:
@my_decorator('arg1 value', 'arg2 value')
def my_function(arg1, arg2, arg3):
print('Inside the function')
print('Function arguments:', arg1, arg2, arg3)
This code defines a function named my_function
that accepts three arguments and prints their values. The @my_decorator('arg1 value', 'arg2 value')
syntax applied to the function indicates that the my_decorator
decorator should be applied to the my_function
function, and the specified arguments should be passed to the decorator. This means that whenever the my_function
function is called, the my_decorator
decorator will be applied to it with the specified arguments, and the wrapper
function returned by the decorator will be called instead of the original my_function
function.
If you call the my_function function with the arguments 'arg1 value'
, 'arg2 value'
, 'arg3 value'
, you will see the following output:
Before calling the function
Decorator arguments: arg1 value arg2 value
Inside the function
Function arguments: arg1 value arg2 value arg3 value
After calling the function
This output shows the messages printed by the wrapper
function before and after calling the original my_function
function, as well as the values of the arg1
and arg2
arguments passed to the decorator and the values of the arg1
, arg2
, and arg3
arguments passed to the my_function
function. This demonstrates how decorator arguments can be used to customize the behavior of a decorator for each function it is applied to.
Decorator Classes
In addition to decorator functions, Python also supports decorator classes. Decorator classes are classes that implement the __call__
method, which allows them to be called like a function. This makes it possible to define a decorator as a class, rather than a function.
Here is an example of a decorator class in Python:
class MyDecorator:
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
def __call__(self, func):
def wrapper(*args, **kwargs):
print('Before calling the function')
print('Decorator arguments:', self.arg1, self.arg2)
func(*args, **kwargs)
print('After calling the function')
return wrapper
This MyDecorator
class defines a constructor that accepts two arguments, arg1
and arg2
, and stores them as instance variables. The class also defines the __call__
method, which is called when the decorator is applied to a function. This method accepts a function as an argument and returns a modified version of that function. The modified version, which is defined as the wrapper
function, first prints a message, then prints the values of the arg1
and arg2
instance variables, then calls the original function, and then prints another message.
To use this decorator class, you would apply it to a function using the @
symbol and specify the arguments for the decorator, like this:
@MyDecorator('arg1 value', 'arg2 value')
def my_function(arg1, arg2, arg3):
print('Inside the function')
print('Function arguments:', arg1, arg2, arg3)
This code defines a function named my_function
that accepts three arguments and prints their values. The @MyDecorator('arg1 value', 'arg2 value')
syntax applied to the function indicates that the MyDecorator
decorator class should be applied to the my_function
function, and the specified arguments should be passed to the constructor. This means that whenever the my_function
function is called, an instance of the MyDecorator
class will be created with the specified arguments, and the __call__
method of the instance will be called, which will return the wrapper
function and replace the original my_function
function.
If you call the my_function
function with the arguments 'arg1 value'
, 'arg2 value'
, 'arg3 value'
, you will see the same output as in the previous example, because the behavior of the decorator is the same in both cases.
Conclusion
Decorators are a useful and powerful feature of the Python programming language. They allow you to extend and modify the behavior of existing functions and methods without modifying their code. Decorators are typically defined as functions or classes that accept a function as an argument and return a modified version of that function. Decorators can also accept arguments, which allows you to customize the behavior of the decorator for each function it is applied to.
In this post, we have explored the basics of decorators and demonstrated how to use them in your own Python code. In addition to the examples provided in this post, there are many other ways in which you can use decorators to simplify and improve your code. For example, you can use decorators to:
- Automatically handle common tasks, such as logging, caching, or authentication, without repeating the same code in multiple functions.
- Add new functionality to existing functions or methods without modifying their original code.
- Enhance the readability and maintainability of your code by separating the implementation of common tasks from the main logic of your functions and methods.
Further Reading
If you want to learn more about decorators in Python, here are some resources that you may find useful: