装饰器
器:工具
装饰:在物体不变的基础上,在其表面附属些东西,使其美观
在python中函数装饰器意思是一个函数或类的功能是用来装饰其他函数或类,也就是说这个函数装饰器是用来给函数或类添加其他功能,并在原有物体(要装饰的函数或类)不变的基础上
这里就提到一个原则:开放封闭原则
开放:对扩展功能(增加功能)开放,意思在源代码不做任何改变的情况下,为其增加功能
封闭:对修改源代码是封闭的
讲到这里会有一个大概了理解,函数装饰器说白了就是在不修改被装饰对象的源代码和不修改调用方式的前提下,给被装饰对象添加新功能
那怎么实现,看代码
首先随便写一个函数
def run(company, name, s):
time.sleep(1)
print(f"欢迎来到{company}发布会")
print(f"我是主持人{name}")
print(f"距离发布会还有{s}s")
print("welcome")
time.sleep(2)
然后调用这个函数
run("艾米", "优优", "10")
现在我想给run函数增加额外的功能(计时功能),但又要遵守开放封闭原则(不修改run函数源代码和不修改调用方式)
那我是不是可以这样
start = time.time()
run("艾米", "优优", "10")
end = time.time()
print(end-start)
结果如下
欢迎来到艾米发布会
我是主持人优优
距离发布会还有10s
welcome
3.0217247009277344
这样既不修改源代码页也没有修改调用方式,那就有一个问题,如果在一个程序中要调用这个run100多变,上面的代码我就要写个100多变,这样就有点不现实了,这样代码就显得有点冗余了。
我在换个方式,在写一个计时函数
def cost_time():
start = time.time()
run("艾米", "优优", "10")
end = time.time()
print(end-start)
cost_time()
直接调用cost_time函数,结果就出来了
欢迎来到艾米发布会
我是主持人优优
距离发布会还有10s
welcome
3.0217247009277344
这样写源代码没有被修改,但修改了调用方式(还是不对这个先放一边)。先解决一个问题,仔细看run函数的参数是被写死了,调用到其他地方话参数是要重新传参的;那就要改改cost_time函数了
def cost_time(*args, **kwargs):
start = time.time()
run(*args, **kwargs)
end = time.time()
print(end-start)
在cost_time函数写入不定任意的变量args和kwargs(这里这个两个变量不做详解),那调用的时候直接传参给cost_time函数
cost_time("艾米", "优优", "10")
结果还是一样的
欢迎来到艾米发布会
我是主持人优优
距离发布会还有10s
welcome
3.0217247009277344
还有一个问题,装饰cost_time函数我不想只用在run函数上,还要用在其他函数上,怎么办,这里就会用到闭包(不做详解),上代码
def cost_time(func):
def wraper(*args, **kwargs):
start = time.time()
func(*args, **kwargs)
end = time.time()
print(end-start)
return wraper
这里在这个cost_time函数多加一层函数,直接把被装饰对象当作一个参数传给cost_time函数。这样装饰函数我想用在哪个函数上就用在哪个函数上,调用的时候就这样
run = cost_time(run)
run("艾米", "优优", "10")
结果还是一样
欢迎来到艾米发布会
我是主持人优优
距离发布会还有10s
welcome
3.0217247009277344
又有一个问题,假如run函数有个返回值
def run(company, name, s):
time.sleep(1)
print(f"欢迎来到{company}发布会")
print(f"我是主持人{name}") print(f"距离发布会还有{s}s")
print("welcome")
time.sleep(2)
return 20
调用这个函数
run = cost_time(run)
print(run("艾米", "优优", "10"))
结果如下
欢迎来到艾米发布会
我是主持人优优
距离发布会还有10s
welcome
3.0229713916778564
None
返回值为None,cost_time装饰函数把这个返回值给弄丢了,所以调用的时候返回None(就是没有返回值)。还得修改cost_time装饰函数。
def cost_time(func):
def wraper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print(end-start)
return res
return wraper
再次调用这个函数看看
run = cost_time(run)
print(run("艾米", "优优", "10"))
结果
欢迎来到艾米发布会
我是主持人优优
距离发布会还有10s
welcome
3.0198171138763428
20
好了,返回值有了20,那最后就剩下修改调用方式的问题了,这个问题就很简单了,就是在被装饰对象前加上@装饰函数,就好了,看代码
def cost_time(func):
def wraper(*args, **kwargs):
start = time.time()
res = func(*args, **kwargs)
end = time.time()
print(end-start)
return res
return wraper
@cost_time
def run(company, name, s):
time.sleep(1)
print(f"欢迎来到{company}发布会")
print(f"我是主持人{name}")
print(f"距离发布会还有{s}s")
print("welcome")
time.sleep(2)
return 20
这样就直接调用run函数看看
print(run("艾米", "优优", "10"))
结果
欢迎来到艾米发布会
我是主持人优优
距离发布会还有10s
welcome
3.020705461502075
20
跟上面一样。到此装饰函数一个大概的原理就是这样,后面还有装饰函数的传参,继续完美的封装,装饰函数的叠加等(后面在详解)。大家自己动手试一试,有助于理解。
最后欢迎大家关注+点赞!