十一、装饰器
1、装饰器函数的本质:一个闭包函数
2、装饰器函数的作用:
在不修改原函数及其调用方式的情况下对原函数功能进行扩展
3、语法糖
格式:@装饰器名称
举例1:创建一个计算函数计算时间的闭包函数
import time
def func():
print('11111')
def outter(fun):
def inner():
start = time.time()
fun()
end = time.time()
print(end - start)
return inner
f = outter(func) #如果只进行到这一步,则不会有任何显示
f()
'''
输出如下:
11111
0.0
'''
举例2:使用语法糖修改装饰器
语法糖:@装饰器名称,等价于func=outter(func),所以调用时,只需要是func()就可以调用
def outter(fun):
def inner():
start = time.time()
fun()
end = time.time()
print(end - start)
return inner
@outter
def func():
print('11111')
func()
如上代码所示,最后func()调用时,并不是调用上面的func()函数,@outter等价于func=outter(func),所以最后func()执行时,执行的是outter(func)
举例3:创建一个带返回值的装饰器
def outter(fun):
def inner():
start = time.time()
ret = fun()
end = time.time()
print(end - start)
return ret
return inner
@outter
def func():
print('11111')
return 'hhh'
a=func()
print(a)
'''
输出如下:
11111
0.0
hhh
'''
在func()函数中设置返回值,然后在inner()函数中设置变量ret用来接收func()函数的返回值,然后在inner()函数中返回ret。最后调用这个装饰器时,用一个变量接收,然后输出这个变量
举例4:创建一个带参数的装饰器
def outter(fun):
def inner(a):
start = time.time()
fun(a)
end = time.time()
print(end - start)
return inner
@outter
def func(s):
print(s)
func('lee')
'''
输出如下:
lee
0.0
'''
如上代码所示,func()函数中的s,及其函数中print()中的s,和装饰器中inner()函数中的a,及其函数内的fun()函数中的a,都是形参,在最后调用func(‘lee’)的时候,不管是a还是s都表示为lee。
举例5:定义一个可以传递任意参数的装饰器
def outter(fun):
def inner(*args, **kwargs):
start = time.time()
fun(*args, **kwargs)
end = time.time()
print(end - start)
return inner
@outter
def func(*args, **kwargs):
print(args, kwargs)
func('lee', 21, height=1.78)
'''
输出如下:
('lee', 21) {'height': 1.78}
0.0
'''
如上代码如示,使用*args和**kwargs可以接收任何参数
使用@wraps(func) 解决被装饰函数的信息不能查看问题
from functools import wraps
def outter(fun):
@wraps(fun)
def inner(*args, **kwargs):
start = time.time()
fun(*args, **kwargs)
end = time.time()
print(end - start)
return inner
@outter
def func(*args, **kwargs):
'''
:param args: 使参数变成一个元组,所有传递的参数变成元组的元素
:param kwargs: 使参数变成一个字典,所有传递的参数变成字典的键值对
:return:
'''
print(args, kwargs)
func('lee', 21, height=1.78)
print(func.__doc__)
print(func.__name__)
输出为:
('lee', 21) {'height': 1.78}
0.0
:param args: 使参数变成一个元组,所有传递的参数变成元组的元素
:param kwargs: 使参数变成一个字典,所有传递的参数变成字典的键值对
:return:
func
给函数添加多个装饰器
def test1(fun):
def inner1():
print('start------test1')
fun()
print('end------test1')
return inner1
def test2(fun):
def inner2():
print('start------test2')
fun()
print('end------test2')
return inner2
@test1 # inner1=test1(inner2)
@test2
def func():
print('this is func()')
func()
输出为:
start------test1
start------test2
this is func()
end------test2
end------test1
以前我们学习的程序运行的过程都是从上往下,但是这里的装饰器并不是这样。
它是先运行与被装饰的函数距离更近的装饰器,所以是先运行test2的,此时test2(fun)运行时,其中的fun参数代入的是我们被修饰的函数func(),然后return inner2(),与此同时,计算机把inner2返回给test1(fun)作为参数fun。
所以运行的顺序是,先调动test2(fun),并把func()函数作为fun参数,然后return inner2,这个时候计算机发现test1需要一个参数,就把inner2作为test1(fun)中的fun代入了,然后运行的是test1(inner2)
第一步:test1(inner2)开始运行,输出start------test1。
第二步:然后继续运行,到了fun()这时,此时的fun其实是inner2。所以调动了inner2,输出了start------test2。
第三步:然后到了下一步,就是inner2中的fun()这一步,此时的fun是函数func(),所以输出了this is func().
第四步:然后进行运行,输出了end------test2,与此同时,inner2函数结束了,所以就返回了test1中的inner1函数。
第五步:程序继续运行,输出了end------test1