Python025装饰器

装饰器

不影响你正常工作写代码,但是应试的时候如果你不会就代表你不深入python

先看下面一段代码,(理解函数的覆盖)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def t():
print(1)

def t():
print(2)

t # 代表函数t的内存地址

t() # 执行函数 打印 2

################################################

def xxx(a):
print(a)

xxx = lambda x:x+1

xxx(1) # 打印2 被匿名函数覆盖

重现公司场景

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
############基础平台提供功能############################
def f1():
print(1)
def f2():
print(2)
def f3():
print(3)
def f4():
print(4)
#############业务部门A###########################
f1()
f2()
f3()
f4()
#############业务部门B###########################
f1()
f2()
f3()
f4()

公司发展后发现需要职责分离每个部门分配的功能调用不一样

  • 如A部门只能调用 f1 f2
  • 如B部门只能调用 f3 f4

然后交给你实现一个鉴权功能,你修改后如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def check_login():
#验证1
#验证2
#验证3
pass

def f1():
check_login()
print(1)
def f2():
check_login()
print(2)
def f3():
check_login()
print(3)
def f4():
check_login()
print(4)

最后部门老大说要遵循「开放封闭」原则,最后给了一个代码的实现

  • 封闭:已实现的功能(不该进行更改功能代码的内部)
  • 开放:对扩展开发(改外面)
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
def w1(func):
def inner():
#验证1
#验证2
#验证3
func()
return inner

@w1
def f1():
print(1)
@w1
def f2():
print(2)
@w1
def f3():
print(3)
@w1
def f4():
print(4)


##########################################
@w1是一个语法糖

@w1
def f1():
print(1)
实际上代码是这样
def w1(func):
def inner():
print('鉴权通过')
func()
return inner
程序执行到 @w1的时候
##########################################
#第一步理解
innerFunc = w1(f1)
innerFunc()
##########################################
#第二步理解——原理
f1 = w1(f1)
f1()

装饰器的应用

包裹数据给字符串加上 b标签

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#定义函数:完成包裹数据
def makeBold(fn):
def wrapped():
print("----1---")
return "<b>" + fn() + "</b>"
return wrapped

@makeBold
def test3():
print("----3---")
return "hello world-3"

ret = test3()
print(ret)

思考?多个装饰器的会如何处理

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
#定义函数:完成包裹数据
def makeBold(fn):
def wrapped():
print("----1---")
return "<b>" + fn() + "</b>"
return wrapped

#定义函数:完成包裹数据
def makeItalic(fn):
def wrapped():
print("----2---")
return "<i>" + fn() + "</i>"
return wrapped

@makeBold
@makeItalic
def test3():
print("----3---")
return "hello world-3"

ret = test3()
print(ret) # <b><i>hello world-3</i></b>

###########原理############
1.因为此时还存在装饰器所以不会立刻执行test3的返回结果 return "hello world-3"
2.此时返回的是
def wrapped():
print("----1---")
return "<b>" + fn() + "</b>"
test3 = makeBold(test3)
发现可以把 wrapped 返回了
test3 = makeItalic(test3)

装饰器执行顺序

1
2
3
4
5
6
7
8
9
10
11
def w1(func):
def inner():
#验证1
#验证2
#验证3
func()
return inner

@w1
def f1():
print(1)

@w1这一行的时候 只要python解释器执行到这里,就自动进行装饰,而不是等调用的时候才装饰

如果是多个装饰器

1
2
3
4
5
6
7
8
9
10
@w1
@w2
实际是
1.w2包装了一下
2.w1继续包装了一下


调用的时候
先拆开w1
再拆开w2

装饰器对带有参数的函数的处理

1
2
3
4
5
6
7
8
9
10
def w1(func):
def inner(a,b):
func(a,b)
return inner

@w1
def f1(a,b):
print(a+b)

f1(1,2) # 实际调的是inner(1,2)

如何做到接受任意参数的函数的装饰器

1
2
3
4
5
6
7
8
def w1(func):
def inner(*args,**kwargs):
func(*args,**kwargs)
return inner

@w1
def f1(a,b):
print(a+b)

带返回值的装饰器

又来了一个坑如果f1带有返回值装饰器返回的是什么

1
2
3
4
5
6
7
8
9
10
11
def w1(func):
def inner():
func()
return inner

@w1
def f1():
print('test')
return 'haha'

f1() # None

再看

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def w1(func):
def inner():
print('1')
x = func() # 保存返回值 'haha'
print('2')
return x
return inner

@w1
def f1():
print('test')
return 'haha'

f1() # None

通用装饰器

1
2
3
4
5
6
7
8
9
def w1(func):
def inner(*args,**kwargs):
x =func(*args,**kwargs)
return x
return inner

@w1
def f1(a,b):
return a+b

带参数装饰器

装饰器是一次闭包,如果装饰器带参就在进行一次闭包

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
def func_arg(arg):
def func(functionName):
def func_in():
print("---记录日志-arg=%s--"%arg)
if arg=="heihei":
functionName()
functionName()
else:
functionName()
return func_in
return func

#1. 先执行func_arg("heihei")函数,这个函数return 的结果是func这个函数的引用
#2. @func
#3. 使用@func对test进行装饰
@func_arg("heihei")
def test():
print("--test--")

#带有参数的装饰器,能够起到在运行时,有不同的功能
@func_arg("haha")
def test2():
print("--test2--")

test()
test2()