翻译:《实用的Python编程》06_02_Customizing_iteration
目录 | 上一节 (6.1 迭代协议) | 下一节 (6.3 生产者/消费者)
6.2 自定义迭代
本节探究如何使用生成器函数自定义迭代。
问题
假设你想要自定义迭代模式。
例如:倒数:
>>> for x in countdown(10):
... print(x, end=' ')
...
10 9 8 7 6 5 4 3 2 1
>>>
有一个j简单方法可以做到这一点。
生成器
生成器(generator)是定义了迭代的函数:
def countdown(n):
while n > 0:
yield n
n -= 1
示例:
>>> for x in countdown(10):
... print(x, end=' ')
...
10 9 8 7 6 5 4 3 2 1
>>>
任何使用了 yield 语句的函数称为生成器。
生成器函数的行为不同于普通于普通函数。调用生成器函数会创建一个生成器对象(generator object),而不是立即执行函数:
def countdown(n):
# Added a print statement
print('Counting down from', n)
while n > 0:
yield n
n -= 1
>>> x = countdown(10)
# There is NO PRINT STATEMENT
>>> x
# x is a generator object
<generator object at 0x58490>
>>>
生成器函数只在 __next__() 方法被调用时才执行:
>>> x = countdown(10)
>>> x
<generator object at 0x58490>
>>> x.__next__()
Counting down from 10
10
>>>
yield 生成一个值,但是挂起(suspend)函数执行。生成器函数会在下次调用 __next__() 方法时恢复(resume),
>>> x.__next__()
9
>>> x.__next__()
8
当生成器返回最后一个值后,再次迭代将会触发一个错误(译注:StopIteration)。
>>> x.__next__()
1
>>> x.__next__()
Traceback (most recent call last):
File "<stdin>", line 1, in ? StopIteration
>>>
观察:生成器函数实现的协议与 for 语句在列表、元组、字典、文件上使用的底层协议相同。
练习
练习 6.4:一个简单的生成器
如果想要自定义迭代,那么你应该始终考虑生成器函数。生成器函数非常容易编写——创建一个函数,执行所需的迭代逻辑,并使用 yield 发送一个值。
例如,创建一个在文件各行中查找匹配子串的生成器:
>>> def filematch(filename, substr):
with open(filename, 'r') as f:
for line in f:
if substr in line:
yield line
>>> for line in open('Data/portfolio.csv'):
print(line, end='')
name,shares,price
"AA",100,32.20
"IBM",50,91.10
"CAT",150,83.44
"MSFT",200,51.23
"GE",95,40.37
"MSFT",50,65.10
"IBM",100,70.44
>>> for line in filematch('Data/portfolio.csv', 'IBM'):
print(line, end='')
"IBM",50,91.10
"IBM",100,70.44
>>>
这是一种有趣的思想——你可以在函数中隐藏自定义的处理过程,并将该函数应用于 for 循环。下一个例子探究一种更不寻常的情况。
练习 6.5:监视流数据源
生成器可应用于监视实时数据源(如:日志文件,股票市场消息)。本部分,我们将对“使用生成器监视实时数据源”这一思想进行探索。首先,请严格遵循以下说明。
Data/stocksim.py 用来模仿股市数据,将实时数据不断地写入到 Data/stocklog.csv 文件。请打开一个独立的命令行窗口,进入到 Data/ 目录,然后运行 stocksim.py 程序:
bash % python3 stocksim.py
如果你使用的是 Windows 系统,那么请找到 stocksim.py 文件,然后双击该文件运行。然后,让我们先把这个程序放到一边(让它一直在那运行),打开另外一个命令行窗口,查看正在被模拟程序(译注:stocksim.py)写入数据的 Data/stocklog.csv 文件(译注:如果使用的是 Linux 系统,那么可以进入到 Data 目录下,然后使用 tail -f stocklog.csv 命令查看)。你应该可以看到每隔几秒新的文本行被添加到 Data/stocklog.csv 文件中。同样,让程序在后台运行——该程序会运行几个小时(对此不用担心)。
stocksim.py 程序运行后,让我们编写一个程序来打开 Data/stocklog.csv 文件、移动到文件末尾、并查看新的输出。请在 Work 目录下创建 follow.py 文件,并把以下代码放入其中:
# follow.py
import os
import time
f = open('Data/stocklog.csv')
f.seek(0, os.SEEK_END) # Move file pointer 0 bytes from end of file
while True:
line = f.readline()
if line == '':
time.sleep(0.1) # Sleep briefly and retry
continue
fields = line.split(',')
name = fields[0].strip('"')
price = float(fields[1])
change = float(fields[4])
if change < 0:
print(f'{name:>10s} {price:>10.2f} {change:>10.2f}')
运行 follow.py 程序,你将会看到实时的股票报价(stock ticker)。 follow.py 里的代码类似于 Unix 系统查看日志文件的 tail -f 命令。
注意事项:在本示例中,readline() 方法的使用与通常从文件中读取行的方式稍微有点不同(通常使用 for 循环)。在这种情况下,我们使用 readline() 来重复探测文件的末尾,以查看是否添加了新的数据(readline() 方法返回新的数据或者空字符串)。
练习 6.6:使用生成器生成数据
查看练习 6.5 中代码你会发现,代码的第一部分产生了几行数据,而 while 循环末尾的语句消费数据。生成器的一个主要特性是你可以将生成数据的代码移动到可重用的函数中。
请修改练习 6.5 的代码,以便通过生成器函数 follow(filename) 执行文件读取。请实现更改以便下面的代码能够工作:
>>> for line in follow('Data/stocklog.csv'):
print(line, end='')
... Should see lines of output produced here ...
请修改股票报价代码,使代码看起来像下面这样:
if __name__ == '__main__':
for line in follow('Data/stocklog.csv'):
fields = line.split(',')
name = fields[0].strip('"')
price = float(fields[1])
change = float(fields[4])
if change < 0:
print(f'{name:>10s} {price:>10.2f} {change:>10.2f}')
练习 6.7:查看股票投资组合
请修改 follow.py 程序,以便程序能够查看股票数据流,并打印股票投资组合中的那些股票的信息。示例:
if __name__ == '__main__':
import report
portfolio = report.read_portfolio('Data/portfolio.csv')
for line in follow('Data/stocklog.csv'):
fields = line.split(',')
name = fields[0].strip('"')
price = float(fields[1])
change = float(fields[4])
if name in portfolio:
print(f'{name:>10s} {price:>10.2f} {change:>10.2f}')
注意事项:要想这段代码能够运行, Portfolio 类必须支持 in 运算符。请参阅 练习 6.3 ,确保 Portfolio 类实现了 __contains__() 运算符。
讨论
在这里,你将一个有趣的迭代模式(在文件末尾读取行)移动到函数中。follow()函数现在是可以在任何程序中使用的完全通用的实用程序。例如,你可以使用 follow() 函数查看服务器日志、调试日志、其它类似的数据源。
目录 | 上一节 (6.1 迭代协议) | 下一节 (6.3 生产者/消费者)
注:完整翻译见 https://github.com/codists/practical-python-zh
翻译:《实用的Python编程》06_02_Customizing_iteration的更多相关文章
- 翻译:《实用的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) 面向对象编程是一种将代码组织成对象集合的编程 ...
随机推荐
- 记录一个状压DP用到的骚操作
不断的让i=i^lowbit(i)就可以枚举i二进制里面所有的1 嘛,很显然,怕是我没想到哦
- 微服务架构Day05-SpringBoot之Servlet
旧版 配置嵌入式Servlet容器 SpringBoot默认使用Tomcat作为嵌入式Servlet容器 如何定制和修改Servlet容器相关配置 1.在配置文件中定制和修改Servlet容器有关的配 ...
- HBuilderX All In One
HBuilderX All In One uni-app https://uniapp.dcloud.io/quickstart-hx 目录结构 一个uni-app工程,默认包含如下目录及文件: $ ...
- web online code editor All In One
web online code editor All In One 在线代码编辑器 Monaco Editor 摩纳哥编辑器 ️ 22.1k The Monaco Editor is the code ...
- Self-XSS All In One
Self-XSS All In One Self-XSS(自跨站脚本)攻击 警告! 使用此控制台可能会给攻击者可乘之机,让其利用 Self-XSS(自跨站脚本)攻击来冒充您并窃取您的信息.请勿输入或粘 ...
- yarn global add !== yarn add global
yarn global add !== yarn add global yarn does not exist the --global flag, but exits yarn global com ...
- GeoJSON feature & Mapbox segments
GeoJSON feature & Mapbox segments custom JSON format ??? { "rows": [], "props&quo ...
- WiFi 6 & 5G
WiFi 6 & 5G https://zhuanlan.zhihu.com/p/85509996 https://www.bilibili.com/read/cv3206576/ https ...
- 全网算力总量暴增,SPC能否成为币圈新宠?
据最新数据显示,在经历了本周初(1月11日)的下跌之后,比特币市场在本周四(1月14日)终于出现了反弹并试图突破4万美元,重新向4万美元上方发起挑战. 这也让加密市场的生态建设者重拾信心,重新对数字货 ...
- C++算法代码——质因数分解[NOIP2012普及组]
题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1102 题目描述 已知正整数 n 是两个不同的质数的乘积,试求出较大的那个质数. 输入 ...