装饰器的演变
首先我们来看个例子,在没有装饰器时要在不修改函数代码前提下扩展一个函数功能可以采用如下方式:
#1.定义一个函数,在不修改函数代码的前提下,对函数的功能进行拓展。比如权限验证。
def f1():
print("这里f1函数的功能展示")
#2.定义一个高级函数(闭包)实现对f1()函数进行权限验证。
def fn(f1):
def fc():
print("这里开始对f1函数权限进行验证")
f1()
print("f1函数已经处理完毕了")
return fc
#3.实现:对函数调用,实现对f1()函数调用的权限验证。
t = fn(f1)
t() #t()相当于fn(f1)().表示对fn(f1)里面的函数fc()调用
'''结果如下:
这里开始对f1函数进行权限验证
这里f1函数的功能展示
f1函数已经处理完毕了
'''
如果有多个修饰的函数的话,那上面函数调用麻烦了,需要一层层嵌套,比如:fn(f1)().结构臃肿。有了装饰器后:
# 为了可视化和模块化,对上面的同一个功能的高级装饰函数进行统一标识,达到更好的效果。
def fn(f1):
def fc():
print("这里开始对f1函数权限进行验证")
f1()
print("f1函数已经处理完毕了")
return fc
@fn #这个@fn标识符效果等同于f1=fn(f1)。
def f1():
print("这里f1函数的功能展示")
f1()
'''结果如下:
这里开始对f1函数进行权限验证
这里f1函数的功能展示
f1函数已经处理完毕了
'''
注意这个时候函数不用再这样fn(f1)()的调用了,而是直接使用f1()即可达到fn(f1)()的效果了。因为@fn的效果等同于f1=fn(f1),所以直接调用f1()相当于实现了fn(f1)(),进而达到原来的效果。同样的效果但是代码就简化多了。
单个装饰器修饰函数
import time
# 注意参数传递
def decorator(max):
def _decorator(fun):
def wrapper(*args, **kwargs):
start = time.time()
for i in range(max):
fun(*args, **kwargs)
runtime = time.time() - start
print(runtime)
return wrapper
return _decorator
@decorator(2)
def do_something(name):
for i in range(1000000):
pass
print(name + " is playing game ")
do_something("mark")
就算不调用do_something(“mark”),也会有输出。这是因为python中装饰器是随着程序的加载运行而自动加载的,跟调不调用方法没有关系.所以只要是装饰器内部函数以外的部分都会自动加载执行,不用调用。
1.装饰器是随着程序的执行而加载的,不是调用函数也会自动加载。
2.装饰器原理:@装饰器名(@decorator) 放在一个函数头上相当于将这个函数整体当做参数传递给这个装饰函数去执行
,即等价于do_something=decorator(do_something),装饰器的使用大大简化了程序的代码。
多个装饰器同时修饰函数
#1.定义两个装饰函数,分别给字体进行加粗和倾斜的标签。
def makeBold(fn):
print("BBBBB"*5)
def wrapped1(): #注意为了演示结果这里讲wrapped函数,分为wrapped1,wrapped2
print("bbbbb"*5)
return "<b>" + fn() + "</b>"
return wrapped1
def makeItalic(fn):
print("IIIII"*5)
def wrapped2(): #注意为了演示结果这里讲wrapped函数,分为wrapped1,wrapped2
print("iiiiii" *3)
return "<i>" + fn() + "</i>"
return wrapped2
#2.使用两个装饰器同时装饰一个函数,可以三个,甚至多个。原理一样
@makeBold #注意2.其效果等同于test_B_I=makeBold( makeItalic(test_B_I) )
@makeItalic #注意1.其效果等同于test_B_I=makeItalic(test_B_I)
def test_B_I():
print("test_B_I"*5)
return "this is the test_B_I"
#-----注意下面对被两个装饰器修饰的函数进行调用--------------------
test_B_I() #调用被两个装饰器修饰后的函数test_B_I()
print(test_B_I()) #打印test_B_I的返回值
'''结果如下:
IIIIIIIIIIIIIIIIIIIIIIIII
BBBBBBBBBBBBBBBBBBBBBBBBB
bbbbbbbbbbbbbbbbbbbbbbbbb
iiiiiiiiiiiiiiiiii
test_B_Itest_B_Itest_B_Itest_B_Itest_B_I
<b><i>this is the test_B_I</i></b>
'''
1.当一个函数被多个装饰器装饰时,装饰器的加载顺序是从内到外的(从下往上的)。其实很好理解:装饰器是给函数装饰的,所以要从靠近函数的装饰器开始从内往外加载
2.外层的装饰器,是给里层装饰器装饰后的结果进行装饰。相当于外层的装饰器装饰的函数是里层装饰器的装饰原函数后的结果函数(装饰后的返回值函数)。