目录 | 上一节 (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的更多相关文章

  1. 翻译:《实用的Python编程》InstructorNotes

    实用的 Python 编程--讲师说明 作者:戴维·比兹利(David Beazley) 概述 对于如何使用我的课程"实用的 Python 编程"进行教学的问题,本文档提供一些通用 ...

  2. 翻译:《实用的Python编程》README

    欢迎光临 大约 25 年前,当我第一次学习 Python 时,发现 Python 竟然可以被高效地应用到各种混乱的工作项目上,我立即被震惊了.15 年前,我自己也将这种乐趣教授给别人.教学的结果就是本 ...

  3. 翻译:《实用的Python编程》05_02_Classes_encapsulation

    目录 | 上一节 (5.1 再谈字典) | 下一节 (6 生成器) 5.2 类和封装 创建类时,通常会尝试将类的内部细节进行封装.本节介绍 Python 编程中有关封装的习惯用法(包括私有变量和私有属 ...

  4. 翻译:《实用的Python编程》04_02_Inheritance

    目录 | 上一节 (4.1 类) | 下一节 (4.3 特殊方法) 4.2 继承 继承(inheritance)是编写可扩展程序程序的常用手段.本节对继承的思想(idea)进行探讨. 简介 继承用于特 ...

  5. 翻译:《实用的Python编程》01_02_Hello_world

    目录 | 上一节 (1.1 Python) | 下一节 (1.3 数字) 1.2 第一个程序 本节讨论有关如何创建一个程序.运行解释器和调试的基础知识. 运行 Python Python 程序始终在解 ...

  6. 翻译:《实用的Python编程》03_03_Error_checking

    目录 | 上一节 (3.2 深入函数) | 下一节 (3.4 模块) 3.3 错误检查 虽然前面已经介绍了异常,但本节补充一些有关错误检查和异常处理的其它细节. 程序是如何运行失败的 Python 不 ...

  7. 翻译:《实用的Python编程》03_04_Modules

    目录 | 上一节 (3.3 错误检查) | 下一节 (3.5 主模块) 3.4 模块 本节介绍模块的概念以及如何使用跨多个文件的函数. 模块和导入 任何一个 Python 源文件都是一个模块. # f ...

  8. 翻译:《实用的Python编程》03_05_Main_module

    目录 | 上一节 (3.4 模块) | 下一节 (3.6 设计讨论) 3.5 主模块 本节介绍主程序(主模块)的概念 主函数 在许多编程语言中,存在一个主函数或者主方法的概念. // c / c++ ...

  9. 翻译:《实用的Python编程》04_01_Class

    目录 | 上一节 (3.6 设计讨论) | 下一节 (4.2 继承) 4.1 类 本节介绍 class 语句以及创建新对象的方式. 面向对象编程(OOP) 面向对象编程是一种将代码组织成对象集合的编程 ...

随机推荐

  1. js console API All In One

    js console API All In One const log = console.log; for(const key in console) { log(`navigator.${key} ...

  2. SwiftUI error All In One

    SwiftUI error All In One Instance member xxx cannot be used on type yyy Instance member 'game' canno ...

  3. iOS WebView All In One

    iOS WebView All In One WKWebView / UIWebView Swift Playground //: A UIKit based Playground for prese ...

  4. UX & feedback & instant visual feedback

    UX & feedback & instant visual feedback Select an element on the page https://ant.design/com ...

  5. VS Code Extension

    VS Code Extension https://code.visualstudio.com/api/get-started/your-first-extension xgqfrms 2012-20 ...

  6. GitHub Learning Lab

    GitHub Learning Lab https://lab.github.com/ https://github.community/t5/GitHub-Learning-Lab/bd-p/lea ...

  7. nasm astrstr函数 x86

    xxx.asm: %define p1 ebp+8 %define p2 ebp+12 %define p3 ebp+16 section .text global dllmain export as ...

  8. VAST重磅出击,NGK网络搜索量超越ETH!

    Wechat指数中,NGK超越ETH,NGK搜索指数是157648点位,单日环比上涨11.95%,ETH搜索指数是115604点位,就连区块链标杆的BTC也仅仅只有171669点位,我们可清楚的看到N ...

  9. NGK公链助力医疗行业数据安全

    近年来医疗领域的数据泄露事件时有发生,医疗行业数据面临着医疗数据获得不易及缺乏有效管理和数据安全机制问题.而区块链的去中心化.分布式账本等特点正好契合医疗领域的需求点. 医疗数据市场痛点 一.医疗信息 ...

  10. YFI币之后,BGV能否主宰DeFi 沉浮?

    回望今年,币圈风起云涌,比特币.YFI.BGV等一众数字货币共同打造了火热的币圈景象,在短短的时间里可以说是又形成了新的生态,业内对于BGV等新币种的认可度也达到了新高.2020已经接近尾声,放眼20 ...