- 前言
- 目标读者
- 非目标读者
- 本书的结构
- 以实践为基础
- 硬件
- 杂谈:个人的一点看法
- Python 术语表
- Python 版本表
- 排版约定
- 使用代码示例
- 第一部分 序幕
- 第 1 章 Python 数据模型
- 第二部分 数据结构
- 第 2 章 序列构成的数组
- 第 3 章 字典和集合
- 第 4 章 文本和字节序列
- 第三部分 把函数视作对象
- 第 5 章 一等函数
- 第 6 章 使用一等函数实现设计模式
- 第 7 章 函数装饰器和闭包
- 第四部分 面向对象惯用法
- 第 8 章 对象引用、可变性和垃圾回收
- 第 9 章 符合 Python 风格的对象
- 第 10 章 序列的修改、散列和切片
- 第 11 章 接口:从协议到抽象基类
- 第 12 章 继承的优缺点
- 第 13 章 正确重载运算符
- 第五部分 控制流程
- 第 14 章 可迭代的对象、迭代器和生成器
- 14.1 Sentence 类第1版:单词序列
- 14.2 可迭代的对象与迭代器的对比
- 14.3 Sentence 类第2版:典型的迭代器
- 14.4 Sentence 类第3版:生成器函数
- 14.5 Sentence 类第4版:惰性实现
- 14.6 Sentence 类第5版:生成器表达式
- 14.7 何时使用生成器表达式
- 14.8 另一个示例:等差数列生成器
- 14.9 标准库中的生成器函数
- 14.10 Python 3.3 中新出现的句法:yield from
- 14.11 可迭代的归约函数
- 14.12 深入分析 iter 函数
- 14.13 案例分析:在数据库转换工具中使用生成器
- 14.14 把生成器当成协程
- 14.15 本章小结
- 14.16 延伸阅读
- 第 15 章 上下文管理器和 else 块
- 第 16 章 协程
- 第 17 章 使用期物处理并发
- 第 18 章 使用 asyncio 包处理并发
- 第六部分 元编程
- 第 19 章 动态属性和特性
- 第 20 章 属性描述符
- 第 21 章 类元编程
- 结语
- 延伸阅读
- 附录 A 辅助脚本
- Python 术语表
- 作者简介
- 关于封面
16.11 延伸阅读
David Beazley 是 Python 生成器和协程的终极权威。他与 Brian Jones 合著的《Python Cookbook(第 3 版)中文版》一书中有很多使用协程编写的诀窍。Beazley 在 PyCon 期间开设的课程兼有深度和广度,因此享有盛名。首先是 PyCon US 2008 期间的“Generator Tricks for Systems Programmers”课程,在 PyCon US 2009 期间又开设了声名远播的“A Curious Course on Coroutines and Concurrency”课程(三个部分的全部视频链接很难找到:第一部分;第二部分;第三部分)。他最新的课程在蒙特利尔 PyCon 2014 期间开设,题为“Generators: The Final Frontier”。在这个课程中,他举了更多并发的例子,因此与本书第 18 章的话题联系更大。他根本不担心学员的大脑会爆炸,因此在“The Final Frontier”课程的最后一部分用协程代替了经典的访问者模式,用于计算算术表达式。
使用协程能以多种新方式组织代码,不过与递归和多态(动态调度)一样,要花点时间才能习惯。James Powell 写了一篇文章,题为“Greedy algorithm with coroutines”。他在这篇文章中使用协程重写了经典的算法。你可能还想浏览 ActiveState Code 诀窍数据库中标记为协程的流行诀窍。
Paul Sokolovsky 为 Damien George 开发的超级精简的 MicroPython(针对微控制器)解释器实现了 yield from 结构。在研究这个特性的过程中,他制作了非常详细的示意图,解说 yield from 结构的工作原理,并在 python-tulip 邮件列表中分享。Sokolovsky 很友好,允许我把那个 PDF 文件复制到本书的网站中,那个文件的固定链接是 http://flupy.org/resources/yield-from.pdf。
写作本书时,只有 asyncio 库本身和使用这个库的代码大量使用 yield from。我花了很多时间,想找到不依赖 asyncio 库的 yield from 示例。Greg Ewing(PEP 380 的作者,为 CPython 实现了 yield from)发表了一些 yield from 的使用示例:BinaryTree 类、一个简单的 XML 解析器和一个任务调度程序。
Brett Slatkin 写的《Effective Python:编写高质量 Python 代码的 59 个有效方法》一书中的第 40 条短小精辟,题为“考虑用协程来并发地运行多个函数”(网上有免费的英文版样章)。这一节中使用 yield from 驱动生成器的示例是我见过最棒的:那个示例实现了 John Conway 发明的“生命游戏”,使用协程管理游戏运行过程中各个细胞的状态。该书的随书代码在一个 GitHub 仓库中。我重构了那个“生命游戏”示例——把 Slatkin 书中的函数和类与测试代码分开(原来的代码)。我还编写了 doctest 形式的测试,因此不用运行脚本就能看到各个协程和类的输出。重构后的示例发布在 GitHub Gist 网站上。
还有几个有趣的示例没用 asyncio 库,只用了 yield from:Peter Otten 在 Python Tutor 邮件列表中发布的消息,“Comparing two CSV files using Python”;Ian Ward 以 iPython Notebook 形式发布的“Iterables, Iterators, and Generators”教程,实现的是剪刀石头布游戏。
Guido van Rossum 在 python-tulip Google Group 中发表了一篇内容很长的消息,题为“The difference between yield and yield-from”,值得一读。2009 年 3 月 21 日,Nick Coghlan 在 Python-Dev 邮件列表中发布了带有大量注释的 yield from 扩充实现(https://mail.python.org/pipermail/python-dev/2009-March/087382.html)。在那篇消息中,他写道:
不管人们是否觉得使用 yield from 结构的代码难以理解,也不管人们能否领会协作式多线程相关的概念,yield from 结构底层的精巧处理能实现真正的嵌套生成器。
Yury Selivanov 撰写的“PEP 492—Coroutines with async and await syntax”提议为 Python 增加两个关键字:async 和 await。async 与其他现有的关键字结合使用,用于定义新的语言结构。例如,async def 用于定义协程,async for 用于使用异步迭代器(实现 __aiter__ 和 __anext__ 方法,这是协程版的 __iter__ 和 __next__ 方法)迭代可迭代的异步对象。为了避免与即将引入的 async 关键字冲突,asyncio.async() 函数将在 Python 3.4.4 中重命名为 asyncio.ensure_future()。await 关键字的作用与 yield from 结构类似,不过只能在以 async def 定义的协程(禁止使用 yield 和 yield from)中使用。PEP 492 使用新句法把发展成类似协程对象的生成器与全新的原生协程对象明确地区分开了。得益于 async 和 await 关键字,以及几个特殊的新方法,Python 语言将对原生的协程对象提供更好的支持。协程已经做好准备,会成为 Python 未来特别重要的特性,因此 Python 语言应该更好地集成协程。
使用离散事件仿真系统做试验是熟悉协作式多任务的好方法。维基百科中的“Discrete event simulation”一文是不错的入门资料。18Ashish Gupta 写的短篇教程“Writing a Discrete Event Simulation: Ten Easy Lessons”说明了如何自己动手(不使用特别的库)编写离散事件仿真系统。那篇教程中的代码使用 Java 编写,因此是基于类的,而且没使用协程,不过可以轻松地移植到 Python。除了代码之外,那篇简短的教程还介绍了离散事件仿真的术语和组件。把 Gupta 教程中的示例转换成 Python 类,然后再转换成利用协程的类,是个很好的练习。
18如今,即使终身教授也同意,维基百科几乎是学习任何计算机科学知识的入门首选。对其他知识而言虽然不是如此,但是在计算机科学这方面,维基百科特别棒。
如果想使用现成的 Python 协程库,可以使用 SimPy。这个库的在线文档中说道:
SimPy 是使用标准的 Python 开发的基于进程的离散事件仿真框架,事件调度程序基于 Python 的生成器实现,因此还可用于异步网络或实现多智能体系统(即可模拟,也可真正通信)。
协程不是特别新的 Python 特性,但是得到异步编程框架支持(Tornado 最先支持)之前,只在较窄的应用领域内使用。Python 3.3 引入的 yield from 结构和 Python 3.4 添加的 asyncio 包可能会提升协程(和 Python 3.4 本身)的使用量。但写作本书时,Python 3.4 发布还不到一年,因此观看 David Beazley 的课程,阅读涉及这个话题的经典实例时,不会有太多内容深入探讨 Python 协程编程。不过,这只是暂时的。
杂谈
raise from lambda
对编程语言来说,关键字的作用是建立控制流程和表达式计算的基本规则。
语言的关键字像是棋盘游戏中的棋子。对国际象棋来说,关键字是♔、♕、、、和;对围棋来说,关键字是●。
国际象棋的棋手实现计划时,有六种类型的棋子可用;而围棋的棋手看起来只有一种类型的棋子可用。可是,在围棋的玩法中,相邻的棋子能构成更大更稳定的棋子,形状各异,不受束缚。围棋棋子的某些排列是不可摧毁的。围棋的表现力比国际象棋强。围棋的开局走法有 361 种,大约有 1e+170 个合规的位置;而国际象棋的开局走法有 20 种,有 1e+50 个位置。
如果为国际象棋添加一个新棋子,将带来颠覆性的改变;为编程语言添加一个新的关键字也是如此。因此,语言的设计者谨慎考虑引入新关键字是合理的。
表16-1:不同编程语言中的关键字数量
关键字数量 | 语言 | 备注 |
5 | Smalltalk-80 | 以句法极简而著称 |
25 | Go | 编程语言,而不是围棋* |
32 | C | 指 ANSI C。C99 有37 个关键字,C11 有 44 个 |
33 | Python | Python 2.7 有 31 个关键字,Python 1.5 有 28 个 |
41 | Ruby | 关键字可以作为标识符使用(例如,class 也是一个方法的名称) |
49 | Java | 与 C 语言一样,基本类型的名称(char、float 等)是保留字 |
60 | JavaScript | 包含 Java 1.0 的所有关键字,很多都没用(http://mzl.la/1JIr8fM) |
65 | PHP | PHP 5.3 之后引入了七个关键字,如 goto、trait 和 yield |
85 | C++ | 据 cppreference.com 网站给出的信息,C++11 在现有的 75 个关键字的基础上添加了 10 个 |
555 | COBOL | 这不是我捏造的。参见 IBM ILE COBOL 手册 |
∞ | Scheme | 任何人都能定义新关键字 |
* 围棋的英文是 Go,因此作者备注这里说的是 Go 语言。——译者注
Python 3 添加了 nonlocal 关键字,把 None、True 和 False 提升为关键字,废弃了 print 和 exec。在语言的发展过程中,弃用关键字十分罕见。表 16-1 列出了几门语言,按照关键字的数量排序。
Scheme 继承了 Lisp 的宏,允许任何人创建特殊的形式,为语言添加新的控制结构和计算规则。用户定义的这种标识符叫作“句法关键字”。Scheme R5RS 标准声称,“这门语言没有保留的标识符”(标准的第 45 页,但是 MIT/GNU Scheme 这种特殊的实现预定义了 34 个句法关键字,例如 if、lambda 和 define-syntax(用于创建新关键字的关键字)。19
Python 像国际象棋,而 Scheme 像围棋。
现在,回到 Python 句法。我觉得 Guido 对关键字的态度过于保守了。关键字的数量应该少,添加新关键字可能会破坏大量代码,但是在循环中使用 else 揭示了一个递归问题:在更适合使用新关键字的地方重用现有的关键字。在 for、while 和 try 的上下文中,应该使用 then 关键字,而不该妄用 else。
在这个问题上,最严重的一点是重用 def。现在,这个关键字用于定义函数、生成器和协程,而这些对象之间的差异很大,不应该使用相同的句法声明。20
引入 yield from 句法尤其让人失望。再次声明,我觉得真的应该为 Python 使用者提供新的关键字。更糟的是,这开启了新的趋势:把现有的关键字串起来,创建新的句法,而不添加描述性的合理关键字。恐怕有一天我们要苦苦思索 raise from lambda 是什么意思。
突发新闻
完成本书的技术审校之后,Yury Selivanov 提交的“PEP 492 — Coroutines with async and await syntax”好像要被接受了,将在 Python 3.5 中实现。21Guido van Rossum 和 Victor Stinner 都支持这个 PEP,前者是 Python 语言的创造者,后者是 asyncio 库的主要维护者,而 asyncio 库将是新句法的主要使用案例。回应 Selivanov 在 Python-ideas 邮件列表中发布的消息时,Guido 甚至暗示,为了实现这个 PEP,可能会延迟发布 Python 3.5。
当然,这会平息前一节所述的大部分抱怨。
19“The Value Of Syntax?”一文对可扩展的句法和编程语言的可用性做了有趣的探讨。Lambda the Ultimate 讨论组是编程语言极客的度假胜地。
20JavaScript、Python 和其他语言都有这样的问题。推荐阅读 Bob Nystrom 写的“What Color Is Your Function?”一文。
21Python 3.5 已经接受了 PEP 492,增加了两个关键字:async 和 await。——编者注
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论