Decorator

The previous article learned about closures by solving a requirement problem. This article will do the same, by slowly evolving a requirement and understanding Python decorators step by step.

First of all, there is such a function that outputs employee clock-in information:

def punch():
    print('昵称:两点水  部门:做鸭事业部 上班打卡成功')
punch()

The output results are as follows:

昵称:两点水  部门:做鸭事业部 上班打卡成功

Then, product feedback, no, there is no specific information on how to clock in at work. Date, plus the specific date of check-in, this should be very simple and can be solved in minutes. Okay, then just add the code to print the date, as follows:

import time
def punch():
    print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
    print('昵称:两点水  部门:做鸭事业部 上班打卡成功')
punch()

The output result is as follows:

2018-01-09
昵称:两点水  部门:做鸭事业部 上班打卡成功

It is okay to change it like this, but it will change the functional structure of the function. When this function was defined, it was to print information about an employee and prompt that the check-in was successful. Now adding the code to print the date may cause a lot of code duplication problems. For example, there is another place that only needs to print employee information and punch in successfully, but no date is required. So do you have to rewrite a function? Moreover, this functional method of printing the current date is frequently used and can be called as a public function to each module method. Of course, this is all considered as an overall project.

In this case, we can use functional programming to modify this part of the code. Because through previous study, we know that Python functions have two characteristics. A function is also an object, and functions can be nested in functions. Then modify the code to look like this:

import time
def punch():
    print('昵称:两点水  部门:做鸭事业部 上班打卡成功')
def add_time(func):
    print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
    func()
add_time(punch)

Output result:

2018-01-09
昵称:两点水  部门:做鸭事业部 上班打卡成功

Did you find that this way, the punch method is not changed, and any function that needs to print the current date can just pass the function into add_time, like this:

import time
def punch():
    print('昵称:两点水  部门:做鸭事业部 上班打卡成功')
def add_time(func):
    print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
    func()
def holiday():
    print('天气太冷,今天放假')
add_time(punch)
add_time(holiday)

Print result:

2018-01-09
昵称:两点水  部门:做鸭事业部 上班打卡成功
2018-01-09
天气太冷,今天放假

Is it very convenient to use function programming? However, every time we call it, we have to pass the original function as a parameter. Is there a better way to implement it? ? Yes, it is the decorator that I will introduce in this article, because the way to write a decorator is actually similar to that of a closure, but there are no free variables. So here is the way to write the decorator in the above code for comparison. What is the difference between writing and functional programming?

import time
def decorator(func):
    def punch():
        print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
        func()
    return punch
def punch():
    print('昵称:两点水  部门:做鸭事业部 上班打卡成功')
f = decorator(punch)
f()

Output results:

2018-01-09
昵称:两点水  部门:做鸭事业部 上班打卡成功

Through the code, you can know that the decorator function generally does these three things:

Receives a function as a parameter

Nesting a wrapper function, the wrapper function will receive the same parameters of the original function, execute the original function, and also perform additional functions

Return to the nested function

However, take a closer look at this code , how come the way of writing this decorator is more troublesome than functional programming. And it looks complicated, even a bit unnecessary.

That’s because we haven’t used the “syntactic sugar” of the decorator yet. Looking at the above code, we can see that when Python introduced the decorator (Decorator), it did not introduce any new syntax features. They were all based on functions. grammatical features. This also shows that decorators are not unique to Python, but a programming idea common to every language. It’s just that Python has designed @ syntax sugar, which makes the process of defining a decorator, calling the decorator the original function and assigning the result to the object name of the original function simpler, more convenient, and easier to operate, so the core of the Python decorator can Saying it is its syntactic sugar.

So how to use its syntax sugar? It's very simple. After writing the decorator function according to the above writing method, directly add @ and the function name of the decorator to the original function. As follows:

import time
def decorator(func):
    def punch():
        print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
        func()
    return punch
@decorator
def punch():
    print('昵称:两点水  部门:做鸭事业部 上班打卡成功')
punch()

Output result:

2018-01-09
昵称:两点水  部门:做鸭事业部 上班打卡成功

Then this is very convenient for our calls. For example, in the example, after using the decorator, directly in the original Just add the syntactic sugar of the decorator to the function. There is no change to this function, and the calling place does not need to be modified.

However, there has always been a problem here, that is, the output of punch-in information is fixed, so we need to pass it through parameters. How to write the decorator? The function in the decorator can use *args variable parameters, but only using *args cannot fully include all parameters. For example, keyword parameters cannot be used. In order to be compatible with keyword parameters, we also need to add **kwargs .

Therefore, the final form of the decorator can be written like this:

import time
def decorator(func):
    def punch(*args, **kwargs):
        print(time.strftime('%Y-%m-%d', time.localtime(time.time())))
        func(*args, **kwargs)
    return punch
@decorator
def punch(name, department):
    print('昵称:{0}  部门:{1} 上班打卡成功'.format(name, department))
@decorator
def print_args(reason, **kwargs):
    print(reason)
    print(kwargs)
punch('两点水', '做鸭事业部')
print_args('两点水', sex='男', age=99)

The output result is as follows:

2018-01-09
昵称:两点水  部门:做鸭事业部 上班打卡成功
2018-01-09
两点水
{'sex': '男', 'age': 99}

At this point, the series of articles on grassroots Python introduction is over.


Continuing Learning
||
submitReset Code