博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
python装饰器系列(四)
阅读量:4620 次
发布时间:2019-06-09

本文共 2384 字,大约阅读时间需要 7 分钟。

带参数的装饰器

先来看一个不带参数的装饰器

1 import time 2  3 def timeit(fn): 4     def wrap(*args,**kwargs): 5         start = time.time() 6         ret = fn(*args,**kwargs) 7         print(time.time() - start) 8         return ret 9     return wrap10  11 12 @timeit13 def sleep(x):14     time.sleep(x)
1 sleep(3)2 3.0034420490264893

这里打印出来的是执行sleep函数所消耗的自然时间,但在执行此函数时所消耗的cpu时间真的有3.0034420490264893秒吗?当然不是。利用time包中的time.clock方法可以计算得到代码执行所消耗cpu的时间,那怎样来修改上边的timeit函数,让其即能计算代码执行的自然时间,也能计算代码执行所消耗cpu的时间?做如下改进:

1 def timeit_1(process_time=False): 2     cacl = time.clock if process_time else time.time 3     def timeit_2(fn): 4         def wrap(*args,**kwargs): 5             start = cacl() 6             ret = fn(*args,**kwargs) 7             print(cacl() - start) 8             return ret 9         return wrap10     return timeit_211 12  13 def sleep_1(x):14     time.sleep(x)
1 timeit_1(True)(sleep_1)(3)2 0.020000000000000018
1 timeit_1(False)(sleep_1)(3)2 3.0038363933563232
1  # 参数process_time是一个默认参数,所以可以不传递值,默认为False    2 timeit_1()(sleep_1)(3)  3 3.003509283065796

调用过程分解:

1 fn1 = timeit_1(True)

调用timeit_1(True),函数return回了timeit_2,并把fn1这个变量指向了调用结果,即指向了timeit_2,这里的timeit_2也是一个函数,此函数接收一个参数

1 fn2 = fn1(sleep_1)

调用fn1(sleep_1),其实就是调用了timeit_2(sleep_1),并把fn2这个变量指向了调用后的结果,即指向了warp,这里的warp也是一个函数,此函数能接收任意的参数

1 fn2(3)2 0.009999999999999787

调用fn2(3),其实是调用了wrap(3),即执行了wrap函数内的语句,此函数内的ret = fn(*args,**kwargs)语句中的fn其实是指向了sleep,所以在执行wrap函数时,sleep_1函数才真正被执行

改进的装饰器装饰一个函数:

1 @timeit_1(False)2 def sleep_2(x):3     time.sleep(x)
1 sleep_2(3)2 3.0039477348327637

计算代码执行的cpu时间

1 @timeit_1(True)2 def sleep_3(x):3     time.sleep(x)
1 sleep_3(3)2 0.0

魔法背后的原理:

其实质就是在没有用魔法的情况下直接timeit_1(True)(sleep_3)(3)。而当使用@这个魔法后,当代码执行到此行时,解析器会执行timeit_1(True),timeit_1实质就是一函数,接收一个参数,并返回一个timeit_2函数。当代码执行到@所在语句时,会把所装饰的sleep_3函数作为一个参数传递给timeit_1(True)的调用结果,即timeit_2这个函数,即sleep_3这个函数已作为一个变量传递给了timeit_2(fn)中的fn参数,并返回了一个wrap函数,在接下的调用sleep_3(3)这个操作,其实此时的sleep_3这个函数已不是原先的def sleep_3(x):中的sleep_3函数,而是一个指向了wrap的函数,wrap函数接收任何参数,所以把当执行sleep_3(3)时,把参数3传递给了wrap函数,并执行内部的代码,内部代码中ret = fn(*args,**kwargs)中的fn函数依赖还是指向原先的sleep_3(x)这个函数。

这里也有一个简单的记忆方式,如果一个函数被装饰器所装饰,在调用这个函数时其实不再是调用表面上看上去的这个函数,以来做说明

1 @timeit_1(True)2 def sleep_3(x):3     time.sleep(x)

当执行到有@魔法所在行时,相当于执行了sleep_3 = timeit_1(True)(sleep_3),即指向了wrap函数,既然sleep_3指向了wrap函数,那我们执行sleep_3(3)时,其实就是在进行wrap(3)这样的函数调用,记住,函数名也是一个变量

 

转载于:https://www.cnblogs.com/tianshug/p/10921898.html

你可能感兴趣的文章
Python项目对接CAS方案
查看>>
mysql产生随机数
查看>>
编程风格
查看>>
熟悉常用的Linux命令
查看>>
易之 - 我是个大师(2014年3月6日)
查看>>
Delphi中窗体的事件
查看>>
file_get_contents()获取https出现这个错误Unable to find the wrapper “https” – did
查看>>
linux vi编辑器
查看>>
js树形结构-----(BST)二叉树增删查
查看>>
contract
查看>>
Python语言编程
查看>>
[poj 1469]Courses
查看>>
vue+element-ui实现表格checkbox单选
查看>>
测试开发学习进阶教程 视频&PDF
查看>>
C#基础-连接Access与SQL Server
查看>>
autofac
查看>>
MacOS 系统终端上传文件到 linux 服务器
查看>>
Excel导出POI
查看>>
兼容性
查看>>
自动执行sftp命令的脚本
查看>>