This is the 13th in a series of Python tutorials; for more, visit my Python collection!
Python decorators are a powerful tool for modifying or enhancing the behavior of a function or method without changing its source code. A decorator is essentially a function that takes a function as an argument and returns a new function. Uses for decorators include logging, performance testing, transaction processing, caching, permission checking, and more!
1 Basic syntax
The basic syntax of a decorator is to precede a function definition with a@
symbol that immediately follows the name of the decorator. Example:
# Define a decorator with arguments to the method being decorated
def my_decorator(func).
def wrapper().
print("before the method runs")
func()
print("after the method is run")
return wrapper
# Use the decorator with "@".
@my_decorator
def say_hello().
print("Hello!")
say_hello()
This code will output:
Before the method runs
Hello!
method is run
2 Parameter Passing
If the decorated function takes arguments, the decorator needs to handle them accordingly:
def my_decorator(func):
def wrapper(name):
print("Before the method runs")
func(name)
print("After the method runs")
return wrapper
@my_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Output:
Before the method runs
Hello, Alice!
After the method runs
Parameters can be more flexible with variable parameters, as follows:
def my_decorator(func):
def wrapper(*args, **kwargs):
print("Before the method runs")
func(*args, **kwargs)
print("After the method runs")
return wrapper
@my_decorator
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
3 Using multiple decorators
You can use multiple decorators for the same function:
def decorator_A(func):
print("enter A")
def wrapper():
print("Before A")
func()
print("After A")
print("out A")
return wrapper
def decorator_B(func):
print("enter B")
def wrapper():
print("Before B")
func()
print("After B")
print("out B")
return wrapper
@decorator_A
@decorator_B
def my_function():
print("Inside my_function")
# Execute the function decorated by the decorator
my_function()
Output:
enter B
out B
enter A
out A
Before A
Before B
Inside my_function
After B
After A
Note the order in which the results are printed.
For ease of expression we'll start by calling decorators that are close to the method being modified inner decorators, such as in the example@decorator_B
The ones that aren't close are called outer decorators, as in the example@decorator_A
。
in closureswrapper
The code outside is the inner decorator executed first, in the closurewrapper
The internal code execution order is a bit more complex:① The outer decorator is executed firstfunc()
Previous code-> ② inner decorator executionfunc()
Previous code ->③Executionfunc()
->④ Inner decorator executionfunc()
Code behind ->⑤ outer decorator executionfunc()
Code behind.
4 Passing parameters to the decorator
The decorator itself can accept parameters, and the behavior of the decorator can be changed depending on the different parameters passed in.
The previous examples are all decorators without parameters, so what if we want to pass parameters to the decorator? So we think about what can receive parameters, the answer is a function. bingo! Python is also designed in this way, we just need to wrap a layer of function outside the decorator, you can pass the parameters to the function and then to the decorator.
A decorator can be defined like this:
def repeat(num_times):
def decorator_repeat(func):
def wrapper(*args, **kwargs):
for _ in range(num_times):
result = func(*args, **kwargs)
return result
return wrapper
return decorator_repeat
@repeat(3)
def greet(name):
print(f"Hello, {name}!")
greet("Alice")
Output:
Hello, Alice!
Hello, Alice!
Hello, Alice!
This defines a decorator that executes a function the specified number of times based on the value passed into the decorator.
5 Classes as Decorators
5.1 __call__
methodologies
Decorators can be not only methods but also classes. This brings us to a special method__call__
。
Python classes that implement the__call__
With this special method, an instance object of the class can be called like a function, because when trying to write the object as a method call (name + ()), the Python interpreter looks for the object's__call__
method and call it.
Here's a simple example that demonstrates__call__
The use of:
class Counter:
def __init__(self):
= 0
def __call__(self, *args, **kwargs):
+= 1
print(f"method is called {} substandard")
counter = Counter()
# analog call
counter()
counter()
counter()
Print:
Method called 1 time
Method called 2 times
Method called 3 times
5.2 Classes as Decorators
One of the main advantages of classes as decorators is that state can be easily maintained, since classes can have instance variables.
get it__call__
After that, we can think of classes as decorators on the principle that the class implements the__call__
method that makes the decorator's code executable.
Below we define a decorator that records the number of function calls:
class CallCounter:
def __init__(self, func):
= func
= 0
def __call__(self, *args, **kwargs):
+= 1
print(f"{.__name__} was called {} substandard")
return (*args, **kwargs)
@CallCounter
def say_hello(name):
print(f"Hello, {name}!")
# Calling a decorated function
say_hello("Alice")
say_hello("Bob")
say_hello("Charlie")
# exports
# say_hello was called 1 substandard
# Hello, Alice!
# say_hello was called 2 substandard
# Hello, Bob!
# say_hello was called 3 substandard
# Hello, Charlie!
Code Explanation:
-
CallCounter
Classes have a constructor__init__
, it must accept a function as an argument. - class implements the
__call__
method, which allows its instances to be called like functions. - exist
__call__
method, the counter is incremented each time the decorated function is calledcount
value and prints the number of times the function has been called. - Finally.
__call__
method calls the original functionand return the result.