python装饰器系列(四)

带参数的装饰器

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

 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 wrap
10
11
12 @timeit
13 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 wrap
10     return timeit_2
11
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

时间: 2024-08-01 11:38:52

python装饰器系列(四)的相关文章

python装饰器系列(二)

对python装饰器系列(一)的deco函数进行修改: 1 def deco(fn): 2 def wrap(): 3 print('ha ha ha') 4 print('call {0} funtion'.format(fn.__name__)) 5 fn() 6 return wrap 1 @deco 2 def myfun(): 3 print('call myfun') 1 myfun() 2 3 ha ha ha 4 call myfun funtion 5 call myfun m

python装饰器系列(五)

带参数的装饰器的应用: 比如有一个函数,只有在对有许可权限的用户开放,执行此函数的用户没有在认证列表里的,就不会执行这个函数.这个该如何实现呢?如下: 1 def check(allow_users): 2 def inner_check(fn): 3 def wrap(username,*args,**kwargs): 4 '''This is wrap''' 5 if username in allow_users: 6 return fn(username,*args,**kwargs)

python装饰器系列(三)

装饰器的应用实例 1 import time 2 def timeit(fn): 3 start = time.time() 4 fn() 5 print(time.time() - start) 6 7 def sleep(): 8 time.sleep(3) 上边代码定义了两个函数,timeit函数能够模拟计算出在执行fn函数所花费的时间 1 timeit(sleep) 2 3.003638505935669 这样来计算一个函数的执行时间是有缺陷的,sleep函数必须是一个接收参数的函数,那

由浅入深,走进Python装饰器-----第四篇:进阶--函数装饰类

**函数装饰器** @函数 类 本篇只是用一个简单的例子,验证函数可以对类进行修饰, 但是由于返回的值为函数,并不是类, 而且改变了原有类的调用方式,所以没有实际应用的意义,就不深入探讨 3.1 用函数装饰器来扩展原类 def KuoZhan(cls): def new_func(): print("类里新增的方法new_func") def new_class(): cls.say = "类里新增的属性" cls.new_func = new_func cls.f

【Python之旅】第四篇(一):Python装饰器

有时候拿到一个程序接口,需要对其进行扩展,但是又不能修改原来接口的源代码,这时候就需要使用装饰器了. 有下面一个小程序,假如是别人提供给我们的调用接口: import time def sayHi():         time.sleep(1)         print 'Hello, I am xpleaf.' 一般情况下,如果想要计算该程序的执行时间(因为有可能要对该接口进行某些性能上的测试),就需要把以上接口修改为下面这样,同时执行一下: 程序代码: import time def s

Python装饰器、迭代器&生成器、re正则表达式、字符串格式化

Python装饰器.迭代器&生成器.re正则表达式.字符串格式化 本章内容: 装饰器 迭代器 & 生成器 re 正则表达式 字符串格式化 装饰器 装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,较为经典的有插入日志.性能测试.事务处理等.装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量函数中与函数功能本身无关的雷同代码并继续重用.概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能. 先定义一个基本的装饰器: ########## 基本装饰器 ########

Python装饰器由浅入深

装饰器的功能在很多语言中都有,名字也不尽相同,其实它体现的是一种设计模式,强调的是开放封闭原则,更多的用于后期功能升级而不是编写新的代码.装饰器不光能装饰函数,也能装饰其他的对象,比如类,但通常,我们以装饰函数为例子介绍其用法.要理解在Python中装饰器的原理,需要一步一步来.本文尽量描述得浅显易懂,从最基础的内容讲起. (注:以下使用Python3.5.1环境) 一.Python的函数相关基础 第一,必须强调的是python是从上往下顺序执行的,而且碰到函数的定义代码块是不会立即执行它的,只

【转】九步学习python装饰器

本篇日志来自:http://www.cnblogs.com/rhcad/archive/2011/12/21/2295507.html 纯转,只字未改.只是为了学习一下装饰器.其实现在也是没有太看明白,对于装饰器我就是用的时候找例子,能蒙对,但是用过之后一段时间就忘了.还是用的少.有空应该好好看一看的,包括闭包.对于各种现代编程语言来说闭包都是很重要的.在这里先谢过原作者,如有侵权请告知. =-=-=-=-=-=-=-=-=-=-一条不怎么华丽的分隔线-=-=-=-=-=-=-=-=-=-= 这

对Python装饰器的个人理解方法

0.说明 在自己好好总结并对Python装饰器的执行过程进行分解之前,对于装饰器虽然理解它的基本工作方式,但对于存在复杂参数的装饰器(装饰器和函数本身都有参数),总是会感到很模糊,即使这会弄懂了,下一次也很快忘记,其实本质上还是没有多花时间去搞懂其中的细节问题. 虽然网络上已经有很多这样的文章,但显然都是别人的思想,因此自己总是记不牢,所以花点时间自己好好整理一下. 最近在对<Python核心编程>做总结,收获了不少,下面分享一下我自己对于Python装饰器的理解,后面还提供了一个较为复杂的P