翻译:《实用的Python编程》07_04_Function_decorators
目录 | 上一节 (7.3 返回函数) | 下一节 (7.5 装饰方法)
7.4 函数装饰器
本节介绍装饰器(decorator)。因为这是一个高级主题,所以我们只做简单介绍。
译注:根据译者个人的猜测,在《设计模式》(《 Design Patterns: Elements of Reusable Object-Oriented Software》)一书中写到 decorator also known as wrapper,所以下文提到包装器(wrapper),其实说的就是装饰器。这里为了保持和原文一致,所以翻译的时候没有将“包装器”替换为“装饰器”。
日志示例
考虑这样一个函数:
def add(x, y):
return x + y
考虑给 add(x, y) 函数添加日志功能:
def add(x, y):
print('Calling add')
return x + y
也带有日志功能的 sub(x, y)函数:
def sub(x, y):
print('Calling sub')
return x - y
观察
观察: 这是一种重复。
在有大量重复代码的地方编写程序通常很烦人。这些代码不仅写起来枯燥,维护起来也很麻烦。尤其是你决定更改其工作方式的时候(例如,可能是另一种类型的日志记录)。
记录日志的代码
也许你可以创建一个添加了日志功能的函数。例如包装器(wrapper):
def logged(func):
def wrapper(*args, **kwargs):
print('Calling', func.__name__)
return func(*args, **kwargs)
return wrapper
使用该函数:
def add(x, y):
return x + y
logged_add = logged(add)
当调用 logged 返回的函数时会发生什么?
logged_add(3, 4) # You see the logging message appear
此示例阐明了创建所谓的包装器函数(wrapper function) 的过程。
包装器是一个函数,它包装了另一个带有额外处理功能的函数,但在其它方面与原始函数的工作方式完全相同。
>>> logged_add(3, 4)
Calling add # Extra output. Added by the wrapper
7
>>>
注意事项:logged() 函数创建了一个包装器,并作为结果返回。
装饰器
在 Python 中,在函数中使用包装器非常常见。因为如此普遍,所以有一个特殊的语法。
def add(x, y):
return x + y
add = logged(add)
# Special syntax
@logged
def add(x, y):
return x + y
该特殊语法执行与上面完全相同的确切步骤。装饰器只是一种新语法,用于装饰函数。
说明
对于装饰器而言,还有许多比这里展示的更微妙的细节,例如,在类里面使用装饰器,或者对同一个函数使用多个装饰器。不过,这里的例子已经很好地说明了如何使用它们。一般而言,它是对出现在各种函数定义中的重复代码的响应。装饰器可以将重复代码移至中心定义。
练习
练习 7.10:计时装饰器
如果你定义了一个函数,那么函数的名称和函数所属模块的名称会分别存储到 __name__ 和 __module__属性中。示例:
>>> def add(x,y):
return x+y
>>> add.__name__
'add'
>>> add.__module__
'__main__'
>>>
请创建 timethis.py 文件,并在文件中编写 timethis(func) 函数。timethis(func) 函数包装一个具有额外逻辑层的函数,逻辑层打印出函数执行所需要的事件。为此,你将在函数中添加如下计时调用。
start = time.time()
r = func(*args,**kwargs)
end = time.time()
print('%s.%s: %f' % (func.__module__, func.__name__, end-start))
(timethis(func))装饰器工作方式示例:
>>> from timethis import timethis
>>> @timethis
def countdown(n):
while n > 0:
n -= 1
>>> countdown(10000000)
__main__.countdown : 0.076562
>>>
讨论:@timethis 装饰器可以放在任何函数的前面,即你应该把装饰器用作性能调优(performance tuning)的诊断工具。
目录 | 上一节 (7.3 返回函数) | 下一节 (7.5 装饰方法)
注:完整翻译见 https://github.com/codists/practical-python-zh
翻译:《实用的Python编程》07_04_Function_decorators的更多相关文章
- 翻译:《实用的Python编程》InstructorNotes
实用的 Python 编程--讲师说明 作者:戴维·比兹利(David Beazley) 概述 对于如何使用我的课程"实用的 Python 编程"进行教学的问题,本文档提供一些通用 ...
- 翻译:《实用的Python编程》README
欢迎光临 大约 25 年前,当我第一次学习 Python 时,发现 Python 竟然可以被高效地应用到各种混乱的工作项目上,我立即被震惊了.15 年前,我自己也将这种乐趣教授给别人.教学的结果就是本 ...
- 翻译:《实用的Python编程》05_02_Classes_encapsulation
目录 | 上一节 (5.1 再谈字典) | 下一节 (6 生成器) 5.2 类和封装 创建类时,通常会尝试将类的内部细节进行封装.本节介绍 Python 编程中有关封装的习惯用法(包括私有变量和私有属 ...
- 翻译:《实用的Python编程》04_02_Inheritance
目录 | 上一节 (4.1 类) | 下一节 (4.3 特殊方法) 4.2 继承 继承(inheritance)是编写可扩展程序程序的常用手段.本节对继承的思想(idea)进行探讨. 简介 继承用于特 ...
- 翻译:《实用的Python编程》01_02_Hello_world
目录 | 上一节 (1.1 Python) | 下一节 (1.3 数字) 1.2 第一个程序 本节讨论有关如何创建一个程序.运行解释器和调试的基础知识. 运行 Python Python 程序始终在解 ...
- 翻译:《实用的Python编程》03_03_Error_checking
目录 | 上一节 (3.2 深入函数) | 下一节 (3.4 模块) 3.3 错误检查 虽然前面已经介绍了异常,但本节补充一些有关错误检查和异常处理的其它细节. 程序是如何运行失败的 Python 不 ...
- 翻译:《实用的Python编程》03_04_Modules
目录 | 上一节 (3.3 错误检查) | 下一节 (3.5 主模块) 3.4 模块 本节介绍模块的概念以及如何使用跨多个文件的函数. 模块和导入 任何一个 Python 源文件都是一个模块. # f ...
- 翻译:《实用的Python编程》03_05_Main_module
目录 | 上一节 (3.4 模块) | 下一节 (3.6 设计讨论) 3.5 主模块 本节介绍主程序(主模块)的概念 主函数 在许多编程语言中,存在一个主函数或者主方法的概念. // c / c++ ...
- 翻译:《实用的Python编程》04_01_Class
目录 | 上一节 (3.6 设计讨论) | 下一节 (4.2 继承) 4.1 类 本节介绍 class 语句以及创建新对象的方式. 面向对象编程(OOP) 面向对象编程是一种将代码组织成对象集合的编程 ...
随机推荐
- 小程序 怎么发 beta 版本
小程序 怎么发 beta 版本 微信 https://developers.weixin.qq.com/miniprogram/dev/devtools/mydev.html 小程序助手 支付宝 ht ...
- ES2020 All in One
ES2020 All in One ES2020 new features / ES11 ES2020 中的10个新功能 1. BigInt BigInt是JavaScript中最令人期待的功能之一, ...
- SSL/TLS协议详解(上):密码套件,哈希,加密,密钥交换算法
本文转载自SSL/TLS协议详解(上):密码套件,哈希,加密,密钥交换算法 导语 作为一名安全爱好者,我一向很喜欢SSL(目前是TLS)的运作原理.理解这个复杂协议的基本原理花了我好几天的时间,但只要 ...
- Linux/UNIX编程如何保证文件落盘
本文转载自Linux/UNIX编程如何保证文件落盘 导语 我们编写程序write数据到文件中时,其实数据不会立马写入磁盘,而是会经过层层缓存.每层缓存都有自己的刷新时机,每层缓存都刷新后才会写入磁盘. ...
- 国际标准时间格式ISO8601
日期表示法 年由4位数字组成YYYY,或者带正负号的四或五位数字表示±YYYYY.以公历公元1年为0001年,以公元前1年为0000年,公元前2年为-0001年,其他以此类推.应用其他纪年法要换算成公 ...
- Java开发工程师最新面试题库系列——Spring部分(附答案)
Spring Spring框架是什么? 答:Spring是轻量级的面向切面和控制反转的框架.初代版本为2002年发布的interface21,Spring框架是为了解决企业级应用开发的复杂性的出现的, ...
- DRF 外键字段深度查询优化、ListSerializer辅助完成群改
目录 一.Response封装 二.外键字段深度查询 1.序列化配置exclude.depth 2.模型层函数.插拔式字段查询 三.listserializer辅助类 一.Response封装 用de ...
- python进阶(11)生成器
生成器 利用迭代器,我们可以在每次迭代获取数据(通过next()方法)时按照特定的规律进行生成.但是我们在实现一个迭代器时,关于当前迭代到的状态需要我们自己记录,进而才能根据当前状态生成下一个数据. ...
- nodejs+express+mongodb实现登录注册
nodejs+express+mongodb实现登录注册 1 简介 登录注册功能使用nodejs+express+mongodb完成,其中对mongodb的操作使用mongoose完成,对mongod ...
- springboot整合jsp,完成公交车站路线图
转: springboot整合jsp,完成公交车站路线图 点赞再看,养成习惯 开发环境: jdk 8 intellij idea tomcat 8 mysql 5.7 maven 3.6 所用技术: ...