- 译者序
- 前言
- 第1章 问答环节
- 第2章 Python 如何运行程序
- 第3章 如何运行程序
- 第4章 介绍 Python 对象类型
- 第5章 数字
- 第6章 动态类型简介
- 第7章 字符串
- 第8章 列表与字典
- 第9章 元组、文件及其他
- 第10章 Python 语句简介
- 第11章 赋值、表达式和打印
- 第12章 if 测试和语法规则
- 第13章 while 和 for 循环
- 第14章 迭代器和解析,第一部分
- 第15章 文档
- 第16章 函数基础
- 第17章 作用域
- 第18章 参数
- 第19章 函数的高级话题
- 第20章 迭代和解析,第二部分
- 第21章 模块:宏伟蓝图
- 第22章 模块代码编写基础
- 第23章 模块包
- 第24章 高级模块话题
- 第25章 OOP:宏伟蓝图
- 第27章 更多实例
- 第28章 类代码编写细节
- 第29章 运算符重载
- 第30章 类的设计
- 第31章 类的高级主题
- 第32章 异常基础
- 第34章 异常对象
- 第35章 异常的设计
- 第36章 Unicode 和字节字符串
- 字符串基础知识
- Python 的字符串类型
- 文本和二进制文件
- Python 3.0 中的字符串应用
- 转换
- 编码 Unicode 字符串
- 编码非ASCII文本
- 编码和解码非ASCII文本
- 其他 Unicode 编码技术
- 转换编码
- 在 Python 2.6 中编码 Unicode 字符串
- 源文件字符集编码声明
- 使用 Python 3.0 Bytes 对象
- 序列操作
- 创建 bytes 对象的其他方式
- 混合字符串类型
- 使用 Python 3.0(和 Python 2.6)bytearray 对象
- 使用文本文件和二进制文件
- Python 3.0 中的文本和二进制模式
- 类型和内容错误匹配
- 使用 Unicode 文件
- 在 Python 3.0 中处理 BOM
- Python 2.6 中的 Unicode 文件
- Python 3.0 中其他字符串工具的变化
- Struct二进制数据模块
- pickle对象序列化模块
- XML解析工具
- 本章小结
- 本章习题
- 习题解答
- 第37章 管理属性
- 第38章 装饰器
- 第39章 元类
- 附录A 安装和配置
- 附录B 各部分练习题的解答
- 作者介绍
- 封面介绍
用迭代工具模拟 zip 和 map
例如,我们已经看到了内置的zip和map函数如何组合可迭代对象和映射函数。使用多个序列参数,map以与zip配对元素相同的方式,把函数映射到取自每个序列的元素。
尽管它们用于不同的目的,如果你研究这些示例足够长的时间,可能会注意到zip结果和执行map的函数参数之间的一种关系,下面的例子可以说明这种关系。
编写自己的map(func,...)
尽管map和zip内置函数快速而方便,总是可以在自己的代码中模拟它们。例如,在上一章中,我们看到一个函数针对单个的序列参数来模拟map内置函数。针对多个序列的时候,也并不会费太多工夫就可以像内置函数那样操作。
这个版本很大程度上依赖于特殊的*args参数传递语法。它收集多个序列(实际上,是可迭代对象)参数,将其作为zip参数解包以便组合,然后成对的zip结果解包作为参数以便传入到函数。也就是说,我们在使用这样的一个事实,zip是map中的一个基本的嵌套操作。最后的测试代码对一个序列和两个序列都应用了这个函数,以产生这一输入(我们可以用内置的map得到同样的输出)。
然而,实际上,前面的版本展示了经典的列表解析模式,在一个for循环中构建操作结果的一个列表。我们可以更精简地编写自己的map,作为单行列表解析的对等体。
当这段代码运行的时候,结果与前面相同,但是,这段代码更加精炼并且可能运行的更快(关于性能的更多讨论,参见本章后面的“对迭代的各种方法进行计时”一节)。之前的mymap版本一次性构建结果列表,并且对于较大的列表来说,这可能浪费内存。既然我们知道了生成器函数和表达式,重新编码这两种替代方案来根据需求产生结果是很容易的。
这些版本产生同样的结果,但是返回设计用来支持迭代协议的生成器。第一个版本每次yield一个结果,第二个版本返回一个生成器表达式的结果来做同样的事情。如果我们把它们包含到一个list调用中迫使它们一次生成所有的值,它们会产生同样的结果。
这里并没有做什么实际工作,直到list调用迫使生成器运行,通过激活迭代协议而进行。生成器由这些函数自身返回,也由它们所使用的Python 3.0式的zip内置函数返回,根据需要产生结果。
编写自己的zip(...)和map(None,...)
当然,目前给出的示例中的很多魔力在于,它们使用zip内置函数来配对来自多个序列的参数。我们也注意到,我们的map近似版确实是模拟了Python 3.0的map的行为,它们从最短的序列的长度处截断,并且,当长度不同的时候,它们不支持补充结果的思路,这就像Python 2.X中带有一个None参数的map所做的一样:
使用迭代工具,我们可以编写近似版来模拟截断的zip和Python 2.6的补充的map,这些其实在代码上近乎是相同的:
这里编写的函数可以在任何类型的可迭代对象上运行,因为它们通过list内置函数来运行自己的参数以迫使结果生成(例如,文件像参数一样工作,此外,序列像字符串一样)。注意这里的all和any内置函数的使用,如果一个可迭代对象中的所有或任何元素为True(或者对等的为非空),它们分别返回True。当列表中的任何或所有参数在删除后变成了空,这些内置函数将用来停止循环。
还要注意Python 3.0的keyword-only参数pad,和Python 2.6的map不同,我们的版本将允许指定任何补充的对象(如果你使用Python 2.6,使用一个**kargs形式来支持这一选项,参见第18章了解详细内容)。当这些函数运行的时候,打印出如下的结果——一个zip和两个补充的map。
这些函数不能够用于列表解析转换,因为它们的循环太具体了。然而,和前面一样,既然我们的zip和map近似版构建并返回列表,用yield将它们转换为生成器以便它们每个都是每次返回结果中的一项,这还是很容易做到的。结果和前面的相同,但是,我们需要再次使用list来迫使该生成器产生其值以供显示。
最后,这里是我们的zip和map模拟器的替代实现——下面的版本不是使用pop方法从列表中删除参数,而是通过计算最小和最大参数长度来完成其工作。有了这些长度,很容易编写嵌套的列表解析来遍历参数索引范围。
由于这些代码使用len和索引,它们假设参数是序列或类似的,而不是任意的可迭代对象。这里,外围的解析遍历参数索引范围,内部的解析(传递到元组)遍历传入的序列以并列地提取参数。当它们运行时,结果和前面相同。
更有趣的是,生成器和迭代器似乎在这个例子中泛滥。传递给min和max的参数是生成器表达式,它在嵌套的解析开始迭代之前运行完成。此外,嵌套的列表解析使用了两个层级的延迟计算——Python 3.0的range内置函数是一个可迭代对象,就像生成器表达式参数对元组。
实际上,这里没有产生结果,直到列表解析的方括号要求放入到结果列表中的值——它们迫使解析和生成器运行。为了把这些函数自身转换为生成器而不是列表构建器,使用圆括号而不是方括号。zip的例子如下所示。
在这个例子中,它用一个list调用来激活生成器和迭代器以产生它们自己的结果。自己体验这些来了解更多内容。进一步开发替代的编码留作一个建议练习(参见下面的“为什么你会留意:单次迭代”来了解这样的一个选项)。
为什么你会留意:单次迭代
在第14章,我们看到了一些内置函数(如map)如何只支持一个单个的便利,并且在发生之后为空,我提过会给出一个示例展示这在实际中是如何变得微妙而重要的。现在,已经学习了关于迭代话题的许多内容,我可以兑现这个承诺了。考虑本章的zip模拟示例的更优替代代码,该示例从Python手册中的一个例子改编而来。
由于这段代码使用iter和next,它对任何类型的可迭代对象都有效。注意,当这个参数的迭代器之一用尽时,没有任何理由捕获由这个解析内的next(i t)来引发的StopIteration——允许它传递会终止这个生成器函数,并且与一条return语句具有相同的效果。如果至少传递了一个参数的话,while iters:对于循环来说足够了,并且,避免了无限循环(列表解析将总是返回一个空的列表):
这段代码在Python 2.6中也工作得很好,如下所示:
但是,在Python 3.0下,它陷入了一个无限循环中并失效,因为Python 3.0的map返回一个单次可迭代对象,而不是像Python 2.6中那样的一个列表。在Python 3.0中,只要我们在循环中运行了一次列表解析,iters将会永远为空(并且res将会是[])。为了使其在Python 3.0下正常工作,我们需要使用list内置函数来创建一个支持多次迭代的对象:
自己运行并跟踪其操作。这里要记住的是:在Python 3.0中把map调用放入到list调用中不仅是为了显示。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论