Decorators have always been a big problem for me. The point is there, but procrastination…
In fact, in the ordinary script writing process, this knowledge point you may not use much
But this is a very common question in an interview.
What is a decorator
The so-called decorator, in fact, is through the decorator function, to modify some functions of the original function, so that the original function does not need to change.
This might not be easy to understand, but let’s start with a stupid function.
No, it’s not “Hello World”!
Def hello(): print(" hello decorator ")Copy the code
What do you think? Are you kidding? Ha ha, this function does not have to run, I’m sure you all know the output: “Hello decorator”.
So if I wanted hello() to do something else, like print one more sentence.
So, you can “enhance” it like this:
Def my_decorator(func): def wrapper(): print(" this is new output after decoration ") def my_decorator(func): def wrapper(): Print (" hello, decorator ") hello = my_decorator(hello)Copy the code
Running results:
This is the new output after decoration hello decoratorCopy the code
Obviously, this “enhancement” doesn’t work, but it helps to understand decorators.
When the last hello() function is run, the call looks like this:
hello = my_decorator(hello)
, the variable Hello refers tomy_decorator()
my_decorator(func)
The biography is refshello
And the returnedwrapper
, so the original function is called againhello()
- So I printed it out first
wrapper()
Function, and then print it outhello()
In the function
My_decorator () in the above code is a decorator. It changes the behavior of Hello (), but it doesn’t really change the internal implementation of hello().
However, Python has a reputation for being “elegant”, and this code is clearly not elegant enough.
Two, elegant decoration
So, to make the above decorator elegant, write:
Def my_decorator(func): def wrapper(): return wrapper @my_decorator def hello(): Print (" Hello decorator ") hello()Copy the code
@my_decorator is the equivalent of the old code hello = my_decorator(hello), and the @ symbol is called syntactic sugar.
If there are other functions that need a similar decorator, just add @my_decorator to the top of the function, greatly improving the reuse and readability of the function.
Def my_decorator(func): def wrapper(): return wrapper @my_decorator def hello(): Def hello2(): print(" hello, decorator ") @my_decorator def hello2(): print(" hello, decorator ")Copy the code
Output:
This is the new output after decoration hello, decorator 2Copy the code
Decorator with parameters
1. Single parameter
The above is a very simple decorator, but in real life, many functions take arguments, such as Hello (People_name).
The wrapper() argument will be added to the wrapper() argument:
def my_decorator(func): def wrapper(people_name): Func (people_name) return wrapper @my_decorator def hello(people_name): Print (" hello, {}". Format (people_name)) print(" hello, {}".Copy the code
Output:
This is the new output after decoration with Hello, Zhang SAN.Copy the code
2. Multiple parameters
But that’s not the end of it. This is simple, but it raises another question: Because not all function arguments are the same, what happens when more than one other function argument uses the decorator? Such as:
@my_decorator def hello3(speaker, listener): print("{} say hello to {} ") .format(speaker, listener))Copy the code
It doesn’t matter. In Python, *args and **kwargs stand for accepting any number and type of arguments, so we can write the Wrapper () function inside the decorator like this:
def my_decorator(func): def wrapper(*args, **kwargs): Return wrapper @my_decorator def hello(people_name): print(" this is new output ") func(*args, **kwargs) return wrapper @my_decorator def hello(people_name): Format (people_name) @my_decorator def hello3(speaker, listener): print("{} say hello to {}. . The format (speaker and the listener)) hello (" wang ") print (" -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- ") hello3 (" zhang ", "li si")Copy the code
Run hello(” Lao Wang “) and hello3(” Zhang SAN “, “Li Si “) simultaneously and see the result:
After this is decorated with a new output hello, Lao wang -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- after this is decorated with a new output Zhang SAN said to li si hello! [Finished in 0.1 s]Copy the code
3. Customize parameters
In both cases, decorators receive external parameters, but decorators can also receive their own parameters. For example, I add a parameter to control the number of times a message is printed in the decorator:
def count(num): def my_decorator(func): def wrapper(*args, **kwargs): for i in range(num): Return my_decorator@count (3) def hello(people_name): int my_decorator@count (3) def hello(people_name): Format (people_name) print(" hello, {}". Format (people_name)) print(" hello, {}".Copy the code
Note that the count decorates the two returns in the function. When run, it should appear three times:
This is the new output after decoration Hello, Lao Wang this is the new output after decoration Hello, Lao WangCopy the code
4. Built-in decorators@functools.wrap
Now, to explore further, let’s print the meta information for the hello() function in the following example:
def my_decorator(func): def wrapper(*args, **kwargs): Return wrapper @my_decorator def hello(people_name): print(" this is new output ") func(*args, **kwargs) return wrapper @my_decorator def hello(people_name): Print (" hello, {}". Format (people_name)) print(hello.__name__) # Look at the meta information for helloCopy the code
Output:
wrapper
Copy the code
This shows that it is no longer the old Hello () function, but has been replaced by the Wrapper () function.
If we need meta-function information, how do we keep it? Use the built-in decorator @functools.wrap.
import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): Return wrapper @my_decorator def hello(people_name): print(" this is new output ") func(*args, **kwargs) return wrapper @my_decorator def hello(people_name): Print (" hello, {} ". The format (people_name) print (hello. __name__)Copy the code
Run:
Hello [Finished in 0.1 s]Copy the code
Four, class decorator
Decorators can be classes as well as functions.
But classes as decorators rely on a function called __call__(), which is executed when an instance of the class is called.
To modify the previous example, change the function decorator to a class decorator:
class MyDecorator(): def __init__(self, func): self.func = func def __call__(self, *args, **kwargs): Return self.func(*args, **kwargs) # def my_decorator(func): # def wrapper(): # func() # return wrapper @mydecorator def hello(): print(" hello, decorator ") hello()Copy the code
Run:
This is the new output after decoration hello decoratorCopy the code
It does the same thing as a function decorator.
Fifth, the nesting of decorators
Since decorators can enhance functions, what if I have multiple decorators that I want? In fact, just add all the decorators you need:
@decorator1
@decorator2
@decorator3
def hello():
...
Copy the code
But notice the order of execution here, which is executed from top to bottom, as you can see:
Def my_decorator(func): def wrapper(): return wrapper def my_decorator2(func): def my_decorator2(func): Def wrapper(): print(" this is the new output 2") func() return wrapper def my_decorator3(func): def wrapper(): Return wrapper @my_decorator@my_decorator2 @my_decorator3 def hello(): Print (" Hello decorator ") hello()Copy the code
run
This is the new output after decoration. This is the new output after decoration. This is the new output after decoration.Copy the code
Bad writing is better than a good memory. It is much better to write and understand.