Python基础(八)装饰器

时间:2022-03-07 11:25:51

今天我们来介绍一下可以提升python代码逼格的东西——装饰器。在学习装饰器之前我们先来复习一下函数的几个小点,方便更好的理解装饰器的含义。

一、知识点复习

1, 在函数中f1和f1()有什么不同,f1:表示的是将整个函数看作一个整体;f1():表示执行f1函数,下面通过一个例子来看一下:

1
2
3
4
5
def f1():
    print('f1')
f1                    #代表函数体本身,什么也不操作
f1()                  #代表执行函数

2、lambda函数:

1
2
3
4
f1 = lambda f1:f1 +100
print(f1(50))
150

3、Python的从上到下的执行方式:

1
2
3
4
5
6
7
def f1():              #根据Python从上而下的原则,先将f1函数加载到内存
    print('f1')        #指向print('f1'),当下面在出现f1函数时指针会指向
def f1():              #新的值,跟变量赋值的原理一样
    print('f2')
f1()
f2

二、装饰器

下面通过一个需求来介绍装饰器在代码中的应用和如何提高代码逼格,需求如下:

B市某创业公司有N个业务部门,1个基础平台部门,基础平台负责提供底层的功能,如:数据库操作、redis调用、监控API等功能。业务部门使用基础功能时,只需要调用基础平台提供的功能即可。具体情况如下:

1
2
3
4
5
6
7
8
#基础部门提供的功能如下:
def f1():
    print('f1')
def f2():
    print('f2')
def f3():
    print('f3')
1
2
3
4
5
#其他业务部门调用的方法如下:
f1()
f2()
f3()

目前公司有条不紊的进行着,但是,以前基础平台的开发人员在写底层代码时没有关注验证相关的问题,即:基础平台的提供的功能可能被任何人使用。现在需要对基础平台的所有功能进行重构,为平台提供的所有功能添加验证机制,即:执行功能前,先进行验证。

现在老大把工作交给了小明,他是这么做的:

1
小明本身比较low,于是去找每个业务部门交涉,每个业务部门自己写代码,调用基础平台的功能之前先验证,这样基础平台就不用修改代码了。

当天小明就被开除了,老大语重心长的说回去好好学学Python吧。

老大又把活交给小明2号 ,小明2号比小明要聪明一点,心想傻X才会一个部门一个部门去找呢,于是重构了基础平台的代码,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#基础平台代码:
def f1():
    #验证
    print('f1')
def f2():
    #验证
    print('f2')
def f3():
    #验证
    print('f3')
#其他业务部门调用基础平台代码不变:
f1()
f2()
f3()

就这样小明2号修改了一周交差了,然而命运总是这么爱捉弄人,几个部门开会说加验证太麻烦,要去掉,又交给了小明2号,这次老大给了排期,一天之内做完,小明2号懵逼了,跟老大说完不成,于是老大去看了小明2号的修改的代码,然后就没有然后了,小明2号也光荣的退出了历史舞台。

经过两次以后,老大已经下定决心要招个牛逼的Python开发,广发英雄帖请来了小明3号,将上面的工作交给了他,下面来看看小明3号是如何修改的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#基础平台提供的功能如下:
def check_login():
    #验证
    pass
def f1():
    check_login()
    print('f1')
def f2():
    check_login()
    print('f2')
...
#其他业务部门调用方法不变:
f1()
f2()
...

修改完以后,老大看完代码很欣慰,对小明3号说:

写代码要遵循开发封闭的原则,虽然在这个原则是用在面向对象开发,但是也适用于函数式编程,简单来说,规定已经实现的功能代码不允许被修改,但可以被扩展,即:

⊙封闭:已经实现的功能代码块

⊙开放:对扩展开放

如果将开放封闭原则应用在上述需求中,那么就不允许在函数f1、f2、f3的内部进行修改代码,老大说现在给你一个举个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def outer(func):
    def inner():
        #check1
        #check2
        return func()
    return inner
@outer
def f1():
    print('f1')
@outer
def f2():
    print('f2')
...

上述代码,也是仅仅对基础平台代码进行修改,就可以实现在其他人调用函数f1、f2、f3之前都进行[验证]操作,并且其他业务部门无需做任何操作。

小明3号有点疑惑的问,这段代码的内部执行原理是什么呢?

老大说想知道,晚上洗干净等着我,这时小明3号菊花一紧,我X,老大还有这癖好,老大:逗你的,实际内部实现很简单,下面给你讲一下:

单独以f1为例:

1
2
3
4
5
6
7
8
9
10
def outer(func):
    def inner():
        #check
        #check
        return func()
    return inner
@outer
def f1():
    print('f1')

Python基础(八)装饰器

当写完这段代码后(函数未被执行),Python解释器会从上到下解释代码,步骤如下:

1,def outer(func):     将outer函数加载到内存

2,@outer                  调用装饰器

从表面上看解释器仅仅会解释两句代码,因为函数在没有被调用之前其内部代码不会被执行,但是

@outer这句代码里却大有文章。

@函数名是Python的一种语法糖:  

1,执行outer函数并将@outer下面的函数作为outer函数的参数,即:@outer 就等价于outer(f1)。

1
2
3
4
5
6
7
def inner():
    #check
    return f1()  #fun是参数,此时就相当于整个f1函数塞进了另一个函数中
return inner     #返回inner,inner代表的是函数,而不是去执行inner函数

2,将执行完的outer函数返回值赋值给@outer下面的函数的函数名。

1
2
3
4
5
6
7
8
9
10
11
12
#outer的返回值为:
def outer(func):
    def inner():
        #check
        return f1()       #此时的func已经是原来的f1函数
    return inner
#然后,将返回值在重新赋值给f1,即:
新f1 = def  inner():
            #check
            return f1()

这样一来,以后业务部门想要执行f1函数时,就会执行新f1函数,在新f1函数内部先验证在执行原来的f1函数,再将原来f1函数的返回值返回给业务调用者。

老大:回去好好研究一下吧,不懂洗干净晚上来我家。。。。

三、装饰器参数

1、被装饰的函数有参数

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
#有一个参数时:
def outer(func):
    def inner(args):
        #check1
        #check2
        return func(args)
    return inner
@outer
def f1(args):
    print('f1')
#有两个参数时:
def outer(func):
    def inner(arg1,arg2):
        #check1
        #check2
        return func(arg1,arg2)
    return inner
@outer
def f1(arg1,arg2):
    print('f1')
#如果有多个参数怎么办,这里就用到了我们之前学的万能参数*args、**kwargs
def outer(func):
    def inner(*args,**kwargs):
        #check1
        #check2
        return func(*args,**kwargs)
    return inner
@outer
def f1(arg1,arg2,arg3,arg4):
    print('f1')

2、一个函数被多个装饰器装饰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def outer1(func):
    def inner(*args,**kwargs):
        #check1
        #check2
        return func(*args,**kwargs)
    return inner
  
def outer2(func):
    def inner(*args,**kwargs):
        #check1
        #check2
        return func(*args,**kwargs)
    return inner
  
@outer1
@outer2
def f1(a​rg1,arg2,arg3,arg4):
    print('f1')

​   3、双重装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def Before(request,kargs):
    print('before')
def After(request,kargs):
    pow('After')
def Filter(brefore_func,after_func):
    def outer(main_func):
        def wrapper(request,kargs):
            brefore_result = brefore_func(request,kargs)
            if (brefore_result != None):
                return brefore_result
            main_result = main_func(request,kargs)
            if (main_result != None):
                return main_result
        return wrapper
    return outer
@Filter(Before,After)
def Index(request,kargs):
    print('index')