python三大器之装饰器详解

时间:2021-09-14 01:31:47

装饰器

讲装饰器之前要先了解两个概念:

  • 对象引用 :对象名仅仅只是个绑定内存地址的变量
?
1
2
3
4
5
6
7
8
9
def func():   # 函数名仅仅只是个绑定内存地址的变量      
    print("i`m running")
# 这是调用                      
func()  # i`m running
# 这是对象引用,引用的是内存地址       
func2 = func 
print(func2 is func)  # True
# 通过引用进行调用 
func2()  # i`m running
  • 闭包:定义一个函数A,然后在该函数内部再定义一个函数B,并且B函数用到了外边A函数的变量
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    def out_func():
        out_a = 10
        def inner_func(inner_x):
            return out_a + inner_x
        return inner_func
     
    out = out_func()
    print(out)  # <function out_func.<locals>.inner_func at 0x7ff378af5c10> out_func返回的是inner_func的内存地址
    print(out(inner_x=2))  # 12

装饰器和闭包不同点在于:装饰器的入参是函数对象,闭包入参是普通数据对象

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def decorator_get_function_name(func):
    """
    获取正在运行函数名
    :return:
    """
    def wrapper(*arg):
        """
        wrapper
        :param arg:
        :return:
        """
        print(f"当前运行方法名:{func.__name__}  with  params: {arg}")
        return func(*arg)
    return wrapper
 
@decorator_get_function_name
def test_func_add(x, y):
    print(x + y)
 
@decorator_get_function_name
def test_func_sub(x, y):
    print(x - y)
 
test_func_add(1, 2)
# 当前运行方法名:test_func_add  with  params: (1, 2)
# 3
test_func_sub(3, 5)
# 当前运行方法名:test_func_sub  with  params: (3, 5)
# -2

常用于如鉴权校验,例如笔者会用于登陆校验:

?
1
2
3
4
5
6
7
8
9
def login_check(func):
    def wrapper(request, *args, **kwargs):
        if not request.session.get('login_status'):
            return HttpResponseRedirect('/api/login/')
        return func(request, *args, **kwargs)
    return wrapper
@login_check
def edit_config():
    pass

装饰器内部的执行逻辑:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
"""
>  1. def login_check(func):  ==>将login_check函数加载到内存
>  ....
>  @login_check  ==>此处已经在内存中将login_check这个函数执行了!;并不需要等edit_config()实例化调用
>  2. 上例@login_check内部会执行以下操作:
>      2.1 执行login_check函数,并将 @login_check 下面的 函数(edit_config) 作为login_check函数的参数,即:@login_check 等价于 login_check(edit_config)
>     2.2 内部就会去执行:
      def wrapper(*args):
          # 校验session...
          return func(request, *args, **kwargs)   # func是参数,此时 func 等于 edit_config,此处相当于edit_config(request, *args, **kwargs)
      return wrapper     # 返回的 wrapper,wrapper代表的是函数对象,非函数实例化对象
      2.3 其实就是将原来的 edit_config 函数塞进另外一个函数中,另一个函数当中可以做一些操作;再执行edit_config
      2.4 将执行完的 login_check 函数返回值(也就是 wrapper对象)将此返回值再重新赋值给新 edit_config,即:
      2.5 新edit_config = def wrapper:
             # 校验session...
            return 原来edit_config(request, *args, **kwargs)
>  3. 也就是新edit_config()=login_check(edit_config):wrapper(request, *args, **kwargs):return edit_config(request, *args, **kwargs) 有点绕,大家看步骤细细理解。
"""

同样一个函数也可以使用多个装饰器进行装饰,执行顺序从上到下

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from functools import wraps
def w1(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("这里是第一个校验")
        return func(*args, **kwargs)
    return wrapper
 
def w2(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print("这里是第二个校验")
        return func(*args, **kwargs)
    return wrapper
 
def w3(func):
    def wrapper(*args, **kwargs):
        print("这里是第三个校验")
        return func(*args, **kwargs)
    return wrapper
 
@w2  # 这里其实是w2(w1(f1))
@w1  # 这里是w1(f1)
def f1():
    print(f"i`m f1, at {f1}")
 
@w3
def f2():
    print(f"i`m f2, at {f2}")
# ====================== 实例化阶段 =====================
f1()
# 这里是第二个校验
# 这里是第一个校验
# i`m f1, at <function f1 at 0x7febc52f5e50>
f2()
# 这里是第三个校验
# i`m f2, at <function w3.<lo

有同学可能要好奇 为什么f1对象打印的是“<function f1 at 0x7febc52f5e50>”,f2对象打印的是“<function w3..wrapper at 0x7febc52f5f70>”(也就是步骤2.5造成的,赋的值是wrapper对象),这就跟w1和w2 内部wrapper使用的wraps装饰器有关系了。

wraps的作用是:被修饰的函数(也就是里面的func)的一些属性值赋值给修饰器函数(wrapper)包括元信息和“函数对象”等。

同时装饰器也可以接受参数:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
def decorator_get_function_duration(enable):
    """
    :param enable:  是否需要统计函数执行耗时
    :return:
    """
    print("this is decorator_get_function_duration")
    def inner(func):
        print('this is inner in decorator_get_function_duration')
        @wraps(func)
        def wrapper(*args, **kwargs):
            print('this is a wrapper in decorator_get_function_duration.inner')
            if enable:
                start = time.time()
                print(f"函数执行前:{start}")
                result = func(*args, **kwargs)
                print('[%s]`s enable was %s it`s duration : %.3f s ' % (func.__name__, enable, time.time() - start))
            else:
                result = func(*args, **kwargs)
            return result
        return wrapper
    return inner
 
def decorator_1(func):
    print('this is decorator_1')
    @wraps(func)
    def wrapper(*args, **kwargs):
        print('this is a wrapper in decorator_1')
        return func(*args, **kwargs)
    return wrapper
 
def decorator_2(func):
    print('this is decorator_2')
    @wraps(func)
    def wrapper(*args, **kwargs):
        print('this is a wrapper in decorator_2')
        return func(*args, **kwargs)
    return wrapper
 
@decorator_1 # 此处相当:decorator_1(decorator_2(decorator_get_function_duration(enable=True)(fun)))
@decorator_2 # = decorator_2(decorator_get_function_duration(enable=True)(fun))
@decorator_get_function_duration(enable=True# = decorator_get_function_duration(enable=True)(fun)
def fun():
    time.sleep(2)
    print("fun 执行完了~")
 
fun()
# ======== enable=False ============
"""
this is decorator_get_function_duration
this is inner in decorator_get_function_duration
this is decorator_2
this is decorator_1
this is a wrapper in decorator_1
this is a wrapper in decorator_2
this is a wrapper in decorator_get_function_duration.inner
fun 执行完了~
"""
# ======== enable=True ============
"""
this is decorator_get_function_duration
this is inner in decorator_get_function_duration
this is decorator_2
this is decorator_1
this is a wrapper in decorator_1
this is a wrapper in decorator_2
this is a wrapper in decorator_get_function_duration.inner
函数执行前:1634635708.648994
fun 执行完了~
[fun]`s enable was True it`s duration : 2.002 s
"""

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注服务器之家的更多内容!

原文链接:https://blog.csdn.net/weixin_45005677/article/details/120707139