1.装饰器
所谓装饰器一般是对已经使用(上线)的函数增加功能.
但是因为一般的大公司的严格按照开放封闭原则(对扩展是开放的,对修改是封闭的),不会让你修改原本的函数.
装饰器就是在不改变原本的函数且不改变原本函数的调用方式上,为原本的函数增加功能的东西.
所以需求就是:
1.不改变原函数
2.不改变原函数的调用方式
3.增加函数的功能
1.1最基本的装饰器
假设存在这样的原函数
1 importtime2 deffunc():3 time.sleep(0.01)4 print("func is running!")
很简单的一个函数,我们现在要为这个原函数增加一个计算函数运行时间的功能.
我们知道计算函数运行时间的函数可以这么写:
1 deftime_func():2 start = time.time() #记录运行前时间
3 func() #调用原函数
4 end = time.time() #记录运行结束时间
5 print(end -start)6
7 time_func()
运行结果:
1 func isrunning!2 0.01015472412109375
这样虽然可以算出函数的运行时间,但是对原函数的调用就完全变了,为了让调用不变,我们可以将装饰器函数写成这样:
1 deftime_func(f):2 definner():3 start = time.time() #记录运行前时间
4 f() #调用原函数
5 end = time.time() #记录运行结束时间
6 print(end -start)7 return inner #返回inner的地址
8
9 print(func) #func函数的地址
10 func = time_func(func) #将func函数的内存地址发给time_func函数
11 print(func) #inner函数的地址
12 func()
运行结果:
1
2 .inner at 0x00000233735867B8>
3 func isrunning!4 0.010173320770263672
可以看到这个装饰器函数实现了通过原函数的调用方式---func()---实现了计算函数的运行时间功能的增加.实现的最基本的装饰器功能.
分析一下实现过程:
1.将func函数的内存地址作为参数传给time_func函数,使inner中的f()就相当于func()调用原函数
2.time_func将inner函数的地址赋值给变量func,让第12行的func()等价于inner()
使得表面上是:
func()调用原函数
实际上是:
func()等价于inner()
inner调用f()
func作为参数传给time_func函数,即f()等价于func()
1.2 有返回值的装饰器
最开始发原函数太简单了,我们给他增加返回值:
1 importtime2 deffunc():3 time.sleep(0.01)4 print("func is running!")5 return "Hello World!"
通过分析,我们知道前面的func()其实是inner(),而inner函数本身没有返回值,我们需要在inner()中增加接受原函数的返回值并返回的功能,可以写作:
1 deftime_func(f):2 definner():3 start = time.time() #记录运行前时间
4 ret = f() #ret接受原函数的返回值原函数 ----增加的行
5 end = time.time() #记录运行结束时间
6 print(end -start)7 return ret #返回原函数的返回值 ----增加的行
8 return inner #返回inner的地址
1 func =time_func(func)2 print(func())
运行结果:
1 func isrunning!2 0.01049661636352539
3 Hello World!
1.3 有参数的装饰器
有的返回值,还缺什么才比较像基本的函数? 参数
先来简单一点的,有限个参数的,原函数写作:
1 importtime2 deffunc(a,b):3 time.sleep(0.01)4 print("func is running!",a,b)5 return "Hello World!"
有参数和返回值不是一样么,在inner函数后面加上参数不就得了,装饰器可以写做:
1 deftime_func(f):2 def inner(a,b): #增加参数 --增加的行
3 start = time.time() #记录运行前时间
4 ret = f(a,b) #将inner函数接到的参数传给原函数 --增加的行
5 end = time.time() #记录运行结束时间
6 print(end -start)7 return ret #返回原函数的返回值
8 return inner #返回inner的地址
1 func =time_func(func)2 print(func("haha","heihei")) #传入haha和heihei
运行结果:
1 func isrunning! haha heihei2 0.010972023010253906
3 Hello World!
成功了!
实际中,原函数的参数可能有很多种,数量也不可能固定不变,不可能原函数的参数一变,你就去改装饰器函数,这样太麻烦
为了解决参数的问题,我们提出王炸策略
*args ------动态参数,可以接受任意数量的位置参数
**kwargs ----动态参数,可以接受任意数量的关键字参数
这种策略在python内置函数中多有用到,比如len函数:
1 def len(*args, **kwargs): #real signature unknown
2 """Return the number of items in a container."""
3 pass
它能接受一切数量的参数,无论是位置参数还是关键字参数,仿照这个,我们可以写出接受任意参数的装饰器函数
原函数:
1 importtime2 def func(*args, **kwargs):3 time.sleep(0.01)4 print("func is running!",*args, **kwargs)5 return "Hello World!"
装饰器函数:
1 deftime_func(f):2 def inner(*args, **kwargs): #增加参数 ----修改部分
3 start = time.time() #记录运行前时间
4 ret = f(*args, **kwargs) #将inner函数接到的参数传给原函数-----修改部分
5 end = time.time() #记录运行结束时间
6 print(end -start)7 return ret #返回原函数的返回值
8 return inner #返回inner的地址
1 #print(func) #func函数的地址
2 func = time_func(func) #将func函数的内存地址发给time_func函数
3 #print(func) #inner函数的地址
4 print(func("haha","heihei",1,2,3,4,5,6,3,2,1))
运行结果:
1 func is running! haha heihei 1 2 3 4 5 6 3 2 1
2 0.010983467102050781
3 Hello World!
1.4 装饰器的固定模式
前面以求运行时间为例,将其进行推广就可以得到装饰器的固定模式:
1 def wrapper(f): #装饰器函数,f是被装饰的函数
2 def inner(*args,**kwargs):3 """在被装饰函数之前要做的事"""
4 ret = f(*args,**kwargs) #被装饰的函数
5 """在被装饰函数之后要做的事"""
6 returnret7 returninner8
9 func = wrapper(func) #感觉有点多余
很明显发现第九行看起来有点多余,于是有了语法糖的概念
语法糖(Syntactic sugar),也译为糖衣语法,是由英国计算机科学家彼得·约翰·兰达(Peter J. Landin)发明的一个术语,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。通常来说使用语法糖能够增加程序的可读性,从而减少程序代码出错的机会。----by百度百科
装饰器的语法糖就是在被装饰函数,也就是原函数的前面加@装饰器函数
像这样:
1 @wrapper2 def func(*args, **kwargs):3 time.sleep(0.01)4 print("func is running!",*args, **kwargs)5 return "Hello World!"
由于@wrapper 等价于func = wrapper(func)
而@wrapper要写在原函数上面,按照python解释器从上到下的工作规律,如果装饰器函数在原函数下面,@wrapper相当于没有意义,会报错,所以装饰器函数必须在原函数之前,整体写做这样:
1 importtime2
3 def wrapper(f): #装饰器函数,f是被装饰的函数
4 def inner(*args,**kwargs):5 """在被装饰函数之前要做的事"""
6 ret = f(*args,**kwargs) #被装饰的函数
7 """在被装饰函数之后要做的事"""
8 returnret9 returninner10
11 @wrapper12 def func(*args, **kwargs):13 time.sleep(0.01)14 print("func is running!",*args, **kwargs)15 return "Hello World!"
16 print(func("haha","heihei",1,2,3,4,5,6,3,2,1))
运行结果:
1 func is running! haha heihei 1 2 3 4 5 6 3 2 1
2 Hello World!