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

import time

def clock(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 in args)
print('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result))
return result
return clocked # ➌

❶ 定义内部函数 clocked,它接受任意个定位参数。
❷ 这行代码可用,是因为 clocked 的闭包中包含自由变量 func。
❸ 返回内部函数,取代被装饰的函数。示例 7-16 演示了 clock 装饰器的用法。
示例 7-16 使用 clock 装饰器

# clockdeco_demo.py

import time
from clockdeco import clock @clock
def snooze(seconds):
time.sleep(seconds) @clock
def factorial(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

工作原理
记得吗,如下代码

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

其实等价于:

def factorial(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__ 属性,会得到如下结果:

>>> import clockdeco_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

import time
import functools def clock(func):
@functools.wraps(func)
def clocked(*args, **kwargs):
t0 = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - t0
name = func.__name__
arg_lst = []
if args:
arg_lst.append(', '.join(repr(arg) for arg in args))
if kwargs:
pairs = ['%s=%r' % (k, w) for k, w in sorted(kwargs.items())]
arg_lst.append(', '.join(pairs))
arg_str = ', '.join(arg_lst)
print('[%0.8fs] %s(%s) -> %r ' % (elapsed, name, arg_str, result))
return result
return clocked

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

python 装饰器(三):装饰器实例(一)的更多相关文章

  1. python项目实战三个小实例

    1.   让用户输入圆的半径,告诉用户圆的面积: import math while True:     # 用户输入     r = input("请输入圆的半径:")     ...

  2. python编码,三个编码实例

    1.字符串编码设置 data = u'你好' utf8 = data.encode('utf-8') 2.管道编码设置 import locale import sys ###设置输出管道编码### ...

  3. 第六种方式,python使用cached_property缓存装饰器和自定义cached_class_property装饰器,动态添加类属性(三),selnium webdriver类无限实例化控制成单浏览器。

    使用 from lazy_object_proxy.utils import cached_property,使用这个装饰器. 由于官方的行数比较少,所以可以直接复制出来用自己的. class cac ...

  4. python装饰器三种装饰模式的简单理解

    学设计模式中有个装饰模式,用java实现起来不是很难,但是远远没有python简单,难怪越来越火了! 这里就简单讨论下python的几种装饰模式: 一 无参装饰器: # 装饰器 import time ...

  5. 函数与装饰器Python学习(三)

    1.1 文件处理 1.1.1 打开文件过程 在Python中,打开文件,得到文件句柄并赋值给一个变量,默认打开模式就为r f=open(r'a.txt','w',encoding='utf-8') p ...

  6. python之循序渐进学习装饰器

    python装饰器的定义:在代码运行期间在不改变原函数定义的基础上,动态给该函数增加功能的方式称之为装饰器(Decorator) 装饰器的优点和用途: 1. 抽离出大量函数中与函数功能本身无关的的雷同 ...

  7. Selenium2+python自动化55-unittest之装饰器(@classmethod)

    前言 前面讲到unittest里面setUp可以在每次执行用例前执行,这样有效的减少了代码量,但是有个弊端,比如打开浏览器操作,每次执行用例时候都会重新打开,这样就会浪费很多时间. 于是就想是不是可以 ...

  8. python设计模式之内置装饰器使用(四)

    前言 python内部有许多内建装饰器,它们都有特别的功能,下面对其归纳一下. 系列文章 python设计模式之单例模式(一) python设计模式之常用创建模式总结(二) python设计模式之装饰 ...

  9. python学习笔记(五):装饰器、生成器、内置函数、json

    一.装饰器 装饰器,这个器就是函数的意思,连起来,就是装饰函数,装饰器本身也是一个函数,它的作用是用来给其他函数添加新功能,比如说,我以前写了很多代码,系统已经上线了,但是性能比较不好,现在想把程序里 ...

  10. Selenium2+python自动化55-unittest之装饰器(@classmethod)【转载】

    前言 前面讲到unittest里面setUp可以在每次执行用例前执行,这样有效的减少了代码量,但是有个弊端,比如打开浏览器操作,每次执行用例时候都会重新打开,这样就会浪费很多时间. 于是就想是不是可以 ...

随机推荐

  1. 关于wifi营销的看过来

    亲测可用.对于一个开发者来说,终于如获至宝.详情联系qq2455994690.源码可二开.包括微信一键关注上网,手机验证码上网.

  2. Cookie和localStorage的查询、设置、修改及删除

    感谢:链接(视频讲解很详细) cookie:是一种字符串表示的数据,用于在本地记录用户的基本信息(账号,密码等),具有时限性. 数据的具体内容: (图源上文视频链接) localStorage:与co ...

  3. [每日一题2020.06.14]leetcode #70 爬楼梯 斐波那契数列 记忆化搜索 递推通项公式

    题目链接 题意 : 求斐波那契数列第n项 很简单一道题, 写它是因为想水一篇博客 勾起了我的回忆 首先, 求斐波那契数列, 一定 不 要 用 递归 ! 依稀记得当年校赛, 我在第一题交了20发超时, ...

  4. 如何使用 Shell 脚本来查看多个服务器的端口是否打开?

    我们在进行服务器配置的时候,经常要查看服务器的某个端口是否已经开放.如果服务器只有一两台的话,那很好办,只需要使用 nc 命令一个个查看即可. 但是,如果你的服务器是个集群,有很多台呢?那如果还一个个 ...

  5. python常见数据类型及操作方法

    title: "python数据类型及其常用方法" date: 2020-04-21T10:15:44+08:00 可变数据类型:允许变量的值发生变化,即如果对变量进行append ...

  6. HTML中doctype的作用及几种类型详解

    一.DOCTYPE标签的定义与作用 <!DOCTYPE>是一个用于声明当前HTMl版本,用来告知web浏览器该文档使用是哪种 HTML 或者 XHTML 规范来解析页面,以便浏览器更加准确 ...

  7. Windows程序设计(2) - API-02 文件系统

    一.磁盘分区的基本概念 1.磁盘分区(Patitions): 分区就是物理存储设备分割成多个不同的逻辑上的存储设备.分区从实质上说就是对硬盘的一种格式化.当我们创建分区时,就已经设置好了硬盘的各项物理 ...

  8. Windows 10 WSL 2.0安装并运行Docker

    在Windows 10 2004版本,微软更新WSL到了2.0,WSL 2.0已经拥有了完整的Linux内核!今天来测试一下,是否可以安装docker!  一.开启WSL 以管理员运行Powershe ...

  9. 关于MySQL事务和存储引擎常见FAQ

    1.什么是事务? 事务就是「一组原子性的SQL查询」,或者说一个独立的工作单元.如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询.如果其中有任何一条语句因为崩溃或其他原因无法 ...

  10. 入门大数据---Hbase搭建

    环境介绍 tuge1 tuge2 tuge3 tuge4 NameNode NameNode DataNode DataNode ZooKeeper ZooKeeper ZooKeeper ZooKe ...