Python学习笔记之装饰器

装饰器简单来说,就是现有的对象,在不修改源代码和调用方式的情况,对现有的对象添加新的功能,比如插入日志,权限校验之类的。

为什么要用装饰器?

1、现在有这么一个对象,我要给这个对象添加一个打印日志的功能

1 def foo():
2     print("I'm foo function")

2、那么可以这样改

1 def foo():
2     print("I'm foo function")
3     print("logging")

但是如果有10个对象,是不是要在10处加上打印日志的代码?那如果是50个,100个呢?这样会有大量重复的代码出现

3、那还可以这样改,把foo当作一个参数传给日志功能不就好了么

1 def foo():
2     print("I'm foo function")
3 
4 def logging(func):
5     print("logging")
6     func()
7 
8 logging(foo)

这样会有一个情况就是,修改了调用方式,假如这段代码已经在生产环境中运行了,而且有很多地方都引用这段代码,不可能把所有已经引用的地方全都修改了吧

这时候就需要用到装饰器了,不用修改原来对象的代码和调用方式,添加新的功能,还可以避免写重复的代码

函数可以赋值给变量

例如

1 def foo():
2     print("I'm foo function")
3 
4 f = foo
5 f()
6 
7 #输出结果
8 I'm foo function

函数名后面有小括号和没有小括号的区别

 1 def foo():
 2     print("I'm foo function")
 3 
 4 print(foo)    #没有小括号表示函数的内存地址
 5 print('---我---是---分---割---线---')
 6 print(foo())  #有小括号表示函数的执行结果
 7 
 8 #输出结果
 9 <function foo at 0x0000026EE94C8F70>
10 ---我---是---分---割---线---
11 I'm foo function
12 None

为什么要说这个函数可以赋值和有没有小括号的问题呢,因为下面会用到这个 

没有参数的对象进行装饰

1、还是这个例子,为其添加日志功能

1 def foo():
2     print("I'm foo function")

2、下面看一下两个例子

(1)语法糖的写法

 1 def logging(func):  #1
 2     def deco():   #3
 3         print("logging")    #6
 4         func()   #7
 5     return deco   #4
 6 
 7 @logging  #2
 8 def foo():
 9     print("I'm foo function")   #8
10 
11 foo()   #5
12 
13 #输出结果
14 logging
15 I'm foo function
(注:#1 #2 #3 ....#8 其表示的意思是在调试模式下的执行顺序)

(2)重新赋值的写法 

 1 def logging(func):    #1
 2     def deco():   #4
 3         print("logging")  #7
 4         func()   #8
 5     return deco   #5
 6 
 7 def foo():   #2
 8     print("I'm foo function")   #9
 9 
10 foo=logging(foo)   #3
11 foo()   #6
12 
13 #输出结果
14 logging
15 I'm foo function
(注:#1 #2 #3 ....#9 其表示的意思是在调试模式下的执行顺序)

这两个例子是等价关系,语法糖@logging的写法,相当于隐式的做了foo=logging(foo)

logging(foo)返回的是deco这个函数的内存地址,被重新赋值给了foo这个函数名,所以现在foo=deco

当执行foo()的时候,其实就是deco()

需要注意的是foo和foo()这两个的区别,foo是表示一个内存地址,而foo()表示的是执行结果

有参数的对象进行装饰

 1 def logging(func):
 2     def deco(*args, **kwargs):   # *args表示可以接收任意个位置参数,**kwargs表示可以接收任意个关键字参数, 位置参数*args要放在关键字参数**kwargs的前面
 3         print("logging")
 4         func(*args, **kwargs)
 5     return deco
 6 
 7 @logging
 8 def foo():
 9     print("My name is foo function")
10 
11 @logging
12 def bar(a, b):
13     print("My name is %s %s"%(a, b))
14 
15 @logging
16 def ten(x, y):
17     print("My name is %s %s"%(x, y))
18 
19 
20 foo('foo')
21 print('---我---是---分---割---线---')
22 bar('bar', 'function')
23 print('---我---是---分---割---线---')
24 ten('ten', y='function')
25 
26 #输出结果
27 logging
28 My name is foo function
29 ---我---是---分---割---线---
30 logging
31 My name is bar function
32 ---我---是---分---割---线---
33 logging
34 My name is ten function

可适用于不带参数的对象、带参数的对象,以及关键字参数的对象的装饰

当然还有一些更高级的用法,比如装饰器也带参数的

可参考以下两个文章:

https://www.cnblogs.com/arvin-feng/p/11108799.html

https://www.liaoxuefeng.com/wiki/1016959663602400/1017451662295584

若以上内容表述有误,欢迎各位大神指导一下。

原文地址:https://www.cnblogs.com/rainights/p/13398247.html