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:

  1. hello = my_decorator(hello), the variable Hello refers tomy_decorator()
  2. my_decorator(func)In returnwrapper(), and is the refshello, so the original function is called againhello()
  3. So I printed it out firstwrapper()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 decorator @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

Bad writing is better than a good memory. It is much better to write and understand. The decorator for the class is also shared below, along with the scene used for the decorator.

In the process of learning Python, I often don’t want to learn it because I don’t have any materials or no one to guide me. You can check my home page, including the supporting materials, Python and Pycharm packages. You can obtain the corresponding materials no matter what stage you are in, thanks for your support!!