1. Core concepts of Python functions
There are several core concepts about functions:
1 in Python, a function is a first-class citizen, and a function is also an object.
We can assign functions to variables, such as the following code:
def func(message): print('Got a message: {}'.format(message)) send_message = func send_message('hello world') # output Got a message: hello world
The function func is assigned to the variable send_message, then call seng_. Message, which is equivalent to calling func().
2 in addition, we can pass a function as a parameter into another function, such as the following code:
def get_message(message): return 'Got a message: ' + message def root_call(func, message): print(func(message)) root_call(get_message, 'hello world') # output Got a message: hello world
3 you can define functions in functions, that is, the nesting of functions.
def func(message): def get_message(message): print('Got a message: {}'.format(message)) return get_message(message) func('hello world') # output Got a message: hello world
4 the return value of a function can also be a function object (closure)
def func_closure(): def get_message(message): print('Got a message: {}'.format(message)) return get_message send_message = func_closure() send_message('hello world') # output Got a message: hello world
2 simple decorator
General function
import time def i_can_sleep(): time.sleep(2) start_time = time.time() i_can_sleep() stop_time = time.time() print("The function is running %s second"%(stop_time-start_time)) Output: The function ran 2.000124454498291 second
If there are a lot of I_ can_ The sleep () function needs to count the running time, so it will write multiple starts_ time = time.time(),stop_time = time.time() is called to do the repeated things only once, and the decorator can be used.
def my_decorator(func): def wrapper(): print('wrapper of decorator') func() # Note that the function object is returned return wrapper def greet(): print('hello world') greet = my_decorator(greet) greet() # output wrapper of decorator hello world
In this code, the variable greet points to the internal function wrapper(), and the internal function wrapper() calls greet(). Therefore, when greet is finally called, it will print "wrapper of decorator" first, then output hello world.
The function here is my_decorator() is a decorator that wraps the function greet() that needs to be executed and changes its behavior, but the original function greet() remains unchanged.
The above code is simpler and more elegant in Python:
def my_decorator(func): def wrapper(): print('wrapper of decorator') func() return wrapper @my_decorator def greet(): print('hello world') greet()
Here @, we call it grammar sugar, @ my_decorator is equivalent to the previous greet=my_decorator(greet) statement, but more concise.
Therefore, if other functions in your program need to be decorated similarly, you only need to add @ decorator above them, which greatly improves the reuse of functions and the readability of the program.
3 trimmer with parameters
You can add corresponding parameters to the corresponding decorator function wrapper(), such as:
def my_decorator(func): def wrapper(message): print('wrapper of decorator') func(message) return wrapper @my_decorator def greet(message): print(message) greet('hello world') # output wrapper of decorator hello world
But if another function needs two arguments, use the decorator my_decorator, there is a problem with the rescue, so usually, we will Variable length parameter *args and kwargs as arguments to wrapper() of decorator's internal function* args and kwargs indicate that any number and type of parameters are accepted, so the decorator can be written in the following form:
def my_decorator(func): def wrapper(*args, **kwargs): print('wrapper of decorator') func(*args, **kwargs) return wrapper
4 decorator with custom parameters
For example, if I want to define a parameter to represent the number of times the decorator's internal function is executed, it can be written in the following form:
def repeat(num): def my_decorator(func): def wrapper(*args, **kwargs): for i in range(num): print('wrapper of decorator') func(*args, **kwargs) return wrapper return my_decorator @repeat(4) def greet(message): print(message) greet('hello world') # Output: wrapper of decorator hello world wrapper of decorator hello world wrapper of decorator hello world wrapper of decorator hello world
5 retain function meta information
Now print the function name:
print(greet.__name__) Output: wrapper
After the greet() function is decorated, its meta information changes. The meta information tells us that "it is no longer the previous green () function, but replaced by the wrapper() function".
In order to solve this problem, we usually use the built-in decorator @ functools.wrap, which helps to retain the meta information of the original function (that is, copy the meta information of the original function to the corresponding decorator function)
import functools def my_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): print('wrapper of decorator') func(*args, **kwargs) return wrapper @my_decorator def greet(message): print(message) print(greet.__name__) # output greet