有借用,但原文出处已经找不到了,根据笔记分享一下解释器的基础。
下面的代码表示,等待两秒钟,输出‘test is running'。
现在要求增加统计程序运行时间的功能。
等待两秒钟,输出‘test is running',现要求增加统计程序运行时间的功能。
import time
def test():
time.sleep(2)
print('test is running!')
test()
学习先问为什么,有什么用,学基础的时候有时不理解为什么简单的事情要搞那么复杂,而对应的实际应用时,往往“这个复杂方法有很多优点和简化了很多事情”。学基础时用简单的例子,只是为了讲明知识点,而不要去关注例子实现的什么功能,这个功能当然是个‘屁’了。
那么,装饰器的实际应用场景:程序已上线或已使用,但需要增加新功能,修改原函数不合理也不科学(这里真的包含了很多背景声音),就要求:
(1)不能修改原功能函数的代码。例子中的功能是非常简单的,要实现当然是直接修改,但现实中一个功能的实现是非常复杂的代码,直接修改是不合理不科学的。
(2)不能修改原函数的调用方式。在符合第一条的情况下,调用时仍然是用test(),即老方法能实现新功能。现实中后面程序在反复用test(),就是要完全不动老代码的基础上,增加一个’装饰器‘更新整个程序。
(3)上述说的老方法是表面上的,这就是装饰器介入的作用。
好,以下开始改造,用试错的方法解释装饰器的实现过程和原理。
满足条件(1)可以增加代码如下:
def deco(func):
start=time.time()
func()
stop=time.time()
print(stop-start)
deco(test)
#把老代码里的test函数作为参数传递给新函数
#(因为是增加新功能,老功能还是要的,所以新代码里肯定要引用老代码的功能,就采取了这种方法)
看上去新功能就实现了,但可以看到调用方式变为了deco(test),即违背了条件(2),以前程序里可能反复调用了test(),都要改成deco(test),不科学。
同时满足条件(2),再改:
思路:产生一个新函数,重新赋值给test,test=某函数,那么后面反复调用的test()就被实质上改过来了,实现功能。
#试一下:
test=deco(test) #计算右边的,是一个两行字符串,赋值给test根本不是一个函数。
test() #因此跳错误码
再试一下:
增加:return func
然后:
test=deco(test)
test()
代码正常运行,但因为return func的缩进和其他代码是同级的,所以deco(test)这步已经把新功能给实现了,又返回了一次test(),结果重复。
把下面完整的代码去试一下,出现了两次test is running ,不合格。
import time
def test():
time.sleep(2)
print('test is running!')
def deco(func):
start=time.time()
func()
stop=time.time()
print(stop-start)
return func
test=deco(test)
test()
思路没有错,上述这里return一个函数的思考过程也是对的,只是返回的方式不对,应当使用’嵌套函数‘。
def timer(func):
def deco():
start=time.time()
func()
stop=time.time()
print(stop-start)
return deco
#把deco()嵌套在timer(func)里,返回一个deco()函数的函数名deco;
#调用timer(func)时,deco()里面的过程代码类似于没有执行的,
#被打包成了一个函数,执行的只是return了这个函数的函数名。
test=timer(test)
#右边代码返回的是一个deco()的函数名deco,deco()打包了作为被参数传入的原test()里的功能,
#同时增加了新功能。
test()
#符合(2)要求,没有改变调用方法,但实质上这句调用的已经是deco()了。
#顶格代码是不能调用经过了二次缩进的嵌套函数的,
#但这里用了deco()用了return,成了闭包函数,就可以被调用了。
至此,本质上是修改了调用函数,但在表面上并未修改调用方式,而且实现了附加功能,需求实现。
真正的装饰器:语法糖
在原test()的上一行加上@timer,删除test=timer(test).
而且要把新增的函数写在原函数的上面
最后代码
import time
def timer(func):
print(func.__name__) #看看打印出来的是什么
def deco():
start=time.time()
func()
stop=time.time()
print(stop-start)
return deco
@timer
def test():
time.sleep(2)
print('test is running')
#test=timer(test)这句不用了,注释掉
test()
过程比较啰嗦,但每一步都看下来后,装饰 器的原理就知道了,涉及到有参数的时候,再另外理解了。