python装饰器实例-python 装饰器(三):装饰器实例(一)

示例 7-15 定义了一个装饰器,它会在每次调用被装饰的函数时计时,然后把经过的时间、传入的参数和调用的结果打印出来。

示例 7-15 一个简单的装饰器,输出函数的运行时间

importtimedefclock(func):def clocked(*args): #➊

t0 =time.perf_counter()

result= func(*args) #➋

elapsed = time.perf_counter() -t0

name= func.__name__arg_str= ','.join(repr(arg) for arg inargs)print('[%0.8fs] %s(%s) -> %r' %(elapsed, name, arg_str, result))returnresultreturn clocked #➌

❶ 定义内部函数 clocked,它接受任意个定位参数。

❷ 这行代码可用,是因为 clocked 的闭包中包含自由变量 func。

❸ 返回内部函数,取代被装饰的函数。示例 7-16 演示了 clock 装饰器的用法。

示例 7-16 使用 clock 装饰器

#clockdeco_demo.py

importtimefrom clockdeco importclock

@clockdefsnooze(seconds):

time.sleep(seconds)

@clockdeffactorial(n):return 1 if n < 2 else n*factorial(n-1)if __name__=='__main__':print('*' * 40, 'Calling snooze(.123)')

snooze(.123)print('*' * 40, 'Calling factorial(6)')print('6! =', factorial(6))

运行示例 7-16 得到的输出如下:

$ python3 clockdeco_demo.py**************************************** Calling snooze(123)

[0.12405610s] snooze(.123) ->None**************************************** Calling factorial(6)

[0.00000191s] factorial(1) -> 1[0.00004911s] factorial(2) -> 2[0.00008488s] factorial(3) -> 6[0.00013208s] factorial(4) -> 24[0.00019193s] factorial(5) -> 120[0.00026107s] factorial(6) -> 720

6! = 720

工作原理

记得吗,如下代码

@clockdeffactorial(n):return 1 if n < 2 else n*factorial(n-1)

其实等价于:

deffactorial(n):return 1 if n < 2 else n*factorial(n-1)

factorial= clock(factorial)

因此,在两个示例中,factorial 会作为 func 参数传给 clock(参见示例 7-15)。然后, clock 函数会返回 clocked 函数,Python 解释器在背后会把 clocked 赋值给 factorial。

其实,导入clockdeco_demo 模块后查看 factorial 的 __name__ 属性,会得到如下结果:

>>> importclockdeco_demo>>> clockdeco_demo.factorial.__name__

'clocked'

>>>

所以,现在 factorial 保存的是 clocked 函数的引用。自此之后,每次调用 factorial(n),执行的都是 clocked(n)。clocked 大致做了下面几件事。

(1) 记录初始时间 t0。

(2) 调用原来的 factorial 函数,保存结果。

(3) 计算经过的时间。

(4) 格式化收集的数据,然后打印出来。

(5) 返回第 2 步保存的结果。

这是装饰器的典型行为:把被装饰的函数替换成新函数,二者接受相同的参数,而且(通常)返回被装饰的函数本该返回的值,同时还会做些额外操作。

示例 7-15 中实现的 clock 装饰器有几个缺点:不支持关键字参数,而且遮盖了被装饰函数的 __name__ 和 __doc__ 属性。示例 7-17 使用

functools.wraps 装饰器把相关的属性从 func 复制到 clocked 中。此外,这个新版还能正确处理关键字参数。

示例 7-17 改进后的 clock 装饰器

#clockdeco2.py

importtimeimportfunctoolsdefclock(func):

@functools.wraps(func)def clocked(*args, **kwargs):

t0=time.time()

result= func(*args, **kwargs)

elapsed= time.time() -t0

name= func.__name__arg_lst=[]ifargs:

arg_lst.append(','.join(repr(arg) for arg inargs))ifkwargs:

pairs= ['%s=%r' % (k, w) for k, w insorted(kwargs.items())]

arg_lst.append(','.join(pairs))

arg_str= ','.join(arg_lst)print('[%0.8fs] %s(%s) -> %r' %(elapsed, name, arg_str, result))returnresultreturn clocked

functools.wraps 只是标准库中拿来即用的装饰器之一。下一节将介绍 functools 模块中最让人印象深刻的两个装饰器:lru_cache 和singledispatch。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值