Table of contents
3. The difference between closures and decorators
1. Understand the usage of decorators through cases
2. Detailed explanation of Python decorator execution order
4. Decorator passing parameters
1. Overview of decorators
A decorator is a function that is used to add additional functionality to other functions, that is, a function that extends the functionality of the original function.
2. Introduction to decorators
1.What is closure
To master decorators, you must first understand closures. After mastering closures, it will be easy to learn decorators.
Closure is to define an internal function in the external function. The internal function refers to the variables in the external function, and the return value of the external function is the internal function. Closures are the bridge between the inside of a function and the outside of the function.
For example: For example, if we call a function x with a return value, function x returns a function y for us. This function y is called a closure .
# 闭包
def out(i): # 一个外层函数
def inner(j): # 内层函数
return i*j
return inner
Closure definition conditions:
- There must be an inline function
- Inline functions must reference variables in external functions
- The return value of an external function must be an embedded function
Case: The function test_in is a closure
def test(number):
print("--1--")
def test_in(number2):
print("--2--")
print(number+number2)
print("--3--")
return test_in
ret = test(100) #先执行第3行,输出"--1--";再执行第5行,函数名(test_in),不执行函数内部;然后执行第10行,输出"--3--";然后返回(test_in)函数给(test)函数,赋值给ret对象。
print("-"*30) #输出”------------------------------“
ret(1) #执行(test_in)函数,执行第6行,输出"--2--";执行第7行,输出"101"(number=100,number2=1)。
ret(100) #执行(test_in)函数,执行第6行,输出"--2--";执行第7行,输出"200"(number=100,number2=100)。
ret(200) #执行(test_in)函数,执行第6行,输出"--2--";执行第7行,输出"300"(number=100,number2=200)。
Output result:
--1--
--3--
------------------------------
--2--
101
--2--
200
--2--
300
Run analysis: First call the external function test and pass in the default value number, and use ret to point to the reference inside the returned function. When ret is called later, calculations will be performed based on calling the external function.
2.What is a decorator?
Decorator means decoration and decoration, and does not change the function of the original program. For example, if my house is not soundproofed, I would add a layer of soundproofing panels to the wall instead of building a new house. In this way, the house is still the same, but a sound insulation panel (decorator) is added to realize the sound insulation function of the house.
The same is true in the program. There will be no changes to the original function, new functions will be added, and the interface when calling the function will not change.
Decorators can be implemented based on functions or classes, and their usage is basically fixed. Its basic steps are:
- Define decorating functions (classes)
- Define business functions
- Add @decorated function name/class name in the previous line of the business function
Case: (Requirement: Add new functions based on the original function)
def w1(func):
"""装饰器函数"""
def inner():
func()
print("这是添加的新功能")
return inner
@w1 # 等同于调用函数的时候上方加上:f1 = w1(f1)
def f1():
"""业务函数"""
print("---f1---")
@w1 # 等同于调用函数的时候上方加上:f2 = w1(f2)
def f2():
"""业务器函数"""
print("---f2---")
f1()
f2()
Output result:
---f1---
这是添加的新功能
---f2---
这是添加的新功能
3. The difference between closures and decorators
Closures pass variables , while decorators pass functions . There is no difference other than that. In other words, decorators are a type of closure, and they are just closures that pass functions .
4. Why use decorators?
Open and closed principle
Open: refers to being open to extended functions Closed
: refers to being closed to modifying the source code
Decorators add new functions to the decorated object without modifying the source code and calling method of the decorated object.
5. Applicable scenarios
Functions define some functionality before and after execution.
3. How to use decorators
1. Understand the usage of decorators through cases
Case:
def w1(fn):
"""装饰器函数"""
print("---正在装饰---")
def inner():
print("---正在验证---")
fn()
return inner
@w1 # 只要Python解释器执行到这个代码,就开始自动进行装饰,而不是等到调用的时候才装饰
def f1():
"""业务函数"""
print("---2---")
return "hello python"
Output result:
---正在装饰---
Run the analysis:
Decoration begins when the code is executed to @w1 . We did not call function f1 but output " ---Decorating--- ". What we called was the decorated result.
Case:
def w1(fn):
"""装饰器函数"""
print("---正在装饰1---")
def inner():
print("---正在验证---")
fn()
return inner
def w2(fn):
"""装饰器函数"""
print("---正在装饰2---")
def inner():
print("---正在验证---")
fn()
return inner
@w1
@w2
def f1():
"""业务函数"""
print("---3---")
Output result:
---正在装饰2---
---正在装饰1---
Run the analysis:
@w1 is at the top, and the following needs to be a function, but below is @w2 . You must wait for @w2 to finish decorating before decorating @w1 , so first output ---Decorating 2--- , and then output ---Decorating 1 --- .
2. Detailed explanation of Python decorator execution order
2.1 Case execution
Case:
def decorator_a(func):
print ('Get in decorator_a')
def inner_a(*args, **kwargs):
print ('Get in inner_a')
return func(*args, **kwargs)
return inner_a
def decorator_b(func):
print ('Get in decorator_b')
def inner_b(*args, **kwargs):
print ('Get in inner_b')
return func(*args, **kwargs)
return inner_b
@decorator_b
@decorator_a
def f(x):
print ('Get in f')
return x * 2
f(1)
Output result:
Get in decorator_a
Get in decorator_b
Get in inner_b
Get in inner_a
Get in f
Run the analysis:
The above code first defines two functions: The function implemented by these two functions is to receive a function as a parameter and then return another created function ;decotator_a, decotator_b
In this created function, call the received function (text is more confusing than code). The last defined function f uses the one defined above as the decorating function.decotator_a, decotator_b
When we call the decorated function f with 1 as the parameter , what is the order (in order to indicate the order of function execution, printout is used to view the order of function execution)? decotator_a, decotator_b
If you judge without thinking based on the bottom-up principle, execute first decorator_a
and then execute , then output will be output first , and then output . However, it is not. decorator_b
Get in decotator_a
Get in inner_a
Get in decotator_b
Get in inner_b
2.2 The difference between functions and function calls
Why execute first inner_b
and then execute inner_a
? In order to completely understand the above problem, we must first distinguish between two concepts: functions and function calls.
In the above example, f is called a function, and f(1) is called a function call. The latter is the result of evaluating the parameters passed in by the former. In Python, a function is also an object, so function f refers to a function object, and its value is the function itself. f(1) is a call to the function, and its value is the result of the call. According to the definition here, f(1) ) has a value of 2. Similarly, taking the above decorator_a
function as an example, it returns a function object inner_a
, which is defined internally. The function func inner_a
is called in and the result of the call to function func is returned as a value.
2.3 The decorator function is executed immediately after the decorated function is defined.
What exactly happens when a decorator decorates a function. Now simplify our example and assume it looks like this:
def decorator_a(func):
print ('Get in decorator_a')
def inner_a(*args, **kwargs):
print ('Get in inner_a')
return func(*args, **kwargs)
return inner_a
@decorator_a
def f(x):
print ('Get in f')
return x * 2
Interpretation:
@decorator_a #等同于调用函数的时候上方加上:f=decorator_a(f)
def f(x):
print ('Get in f')
return x * 2
# 相当于
def f(x):
print ('Get in f')
return x * 2
f = decorator_a(f)
Run the analysis:
When the interpreter executes this code, decorator_a
it has been called. It passes the function name f as a parameter to the formal parameter fun , and returns a function inner_a generated internally , so thereafter f refers to what is decorater_a
returned inside inner_a
. So when the function f is called in the future , it is actually equivalent to a call . The parameters passed to the function f will be passed to the function . When the function is called , the received parameters (function name f ) will be passed to the formal parameter func=f. , the final return is the value of calling function f , so on the outside it looks like calling function f directly . inner_a
inner_a
inner_a
inner_a
2.4 Case execution sequence analysis
When you clarify the above two concepts, you can clearly see what happened in the most original example.
When the interpreter executes the following code, the sums have actually been called in order from bottom to top , and the corresponding sums will be output . At this time, the function f is already equivalent to the function in the function . But because function f has not been called, the function has not been called, and by analogy, the functions inside the function have not been called, so the sum will not be output. decorator_a
decorator_b
Get in decorator_a
Get in decorator_b
decorator_b
inner_b
inner_b
inner_b
inner_a
Get in inner_a
Get in inner_b
Case:
def decorator_a(func):
print ('Get in decorator_a')
def inner_a(*args, **kwargs):
print ('Get in inner_a')
return func(*args, **kwargs)
return inner_a
def decorator_b(func):
print ('Get in decorator_b')
def inner_b(*args, **kwargs):
print ('Get in inner_b')
return func(*args, **kwargs)
return inner_b
@decorator_b
@decorator_a
def f(x):
print ('Get in f')
return x * 2
f(1)
Output result:
Get in decorator_a
Get in decorator_b
Get in inner_b
Get in inner_a
Get in f
Then on line 21, when we call function f passing parameter 1, the function is called ,inner_b
it will print first Get in inner_b
, and then inner_b
the function is called inside the function, inner_a,
so it will print again Get in inner_a
, and then inner_a
the original function f is called inside the function , and use the result as the final return value. At this point you should know why the output is like that, and have a certain understanding of what actually happens in the decorator execution sequence.
2.5 Summary of case execution sequence
-The order in which multiple decorators are executed is to execute the decorators in order from bottom to top, and then execute the function itself.
-The statements between the outer function and the inner function of the decorator are not decorated on the target function, but are additional operations when loading the decorator.
Lines 15~19 are the process of loading the decorator , which is equivalent to executing f=decorator_b(decorator_a(f)) :
- At this time, decorator_a(f) is executed first , and the result is Get in decorator_a , pointing the formal parameter func to function f , and returning the function
inner_a;
- Then execute function decorator_b(inner_a) , the result is output Get in decorator_b , point the formal parameter func to function inner_a , and return function inner_b ;
- Function f itself is equivalent to function inner_b .
Line 21 actually calls the loaded function , replacing the function name f with the function name inner_b .
- At this time, the function inner_b is actually executed ;
- When the function func() is reached, the function inner_a is executed;
- When the function func() is run, the unmodified function f is executed .
4. Decorator passing parameters
Case: Pass 2 parameters
def w1(fn):
"""装饰器函数"""
print("---正在装饰---")
def inner(a, b): # 如果a, b没有定义,那么会导致19行代码调用失败
print("---正在验证---")
fn(a, b) # 如果没有把a, b当实参进行传递,那么会导致调用13行的函数失败
return inner
@w1
def f1(a, b):
"""业务函数"""
print(a + b)
f1(10, 20)
Output result:
---正在装饰---
---正在验证---
30
Case: variable length parameters
def w1(fn):
"""装饰器函数"""
print("---正在装饰---")
def inner(*args, **kwargs): # 如果a, b没有定义,那么会导致19行代码调用失败
print("---正在验证---")
fn(*args, **kwargs) # 如果没有把a, b当实参进行传递,那么会导致调用13行的函数失败
return inner
@w1
def f1(a, b):
"""业务函数"""
print(a + b)
@w1
def f2(a, b, c):
"""业务函数"""
print(a + b + c)
f1(10, 20)
f2(10, 20, 30)
Output result:
---正在装饰---
---正在装饰---
---正在验证---
30
---正在验证---
60
5. Decorator return value
Case:
def w1(fn):
"""装饰器函数"""
print("---正在装饰---")
def inner():
print("---正在验证---")
ret = fn() # 保存返回来的字符串
return ret # 把字符串返回到20行的调用处
return inner
@w1
def test():
"""业务函数"""
print("---test---")
return "这是原函数返回值"
ret = test() # 需要用参数来接收返回值
print(ret)
Output result:
---正在装饰---
---正在验证---
---test---
这是原函数返回值
6. Universal Decorator
Case:
def w1(fn):
"""装饰器函数"""
def inner(*args, **kwargs):
print("---记录日志---")
ret = fn(*args, **kwargs) # 保存返回来的字符串
return ret # 把字符串返回到20行的调用处
return inner
@w1
def test1():
"""不带返回值"""
print("---test1---")
@w1
def test2():
"""带返回值"""
print("---test2---")
return "这是原函数返回值"
@w1
def test3(a):
"""业务函数"""
print("---test3中的数据:%d---" % a)
ret1 = test1()
print(ret1)
ret2 = test2()
print(ret2)
ret3 = test3(10)
print(ret3)
Output result:
---记录日志---
---test1---
None
---记录日志---
---test2---
这是原函数返回值
---记录日志---
---test3中的数据:10---
None
7. Decorator with parameters
Case:
def func_arg(arg):
def func(funtionName):
def func_in():
print("输出给装饰器传入的参数:%s" % arg)
if arg == "hello":
funtionName()
funtionName()
else:
funtionName()
return func_in
return func
@func_arg("hello")
def test():
print("---test---")
@func_arg("haha")
def test2():
print("---test2---")
test()
test2()
Output result:
输出给装饰器传入的参数:hello
---test---
---test---
输出给装饰器传入的
Run the analysis:
- First execute the func_arg("hello") function. The return result of this function is a reference to the func function;
- execute@func;
- Use @func to decorate function test