欢迎光临
我们一直在努力

《流畅的Python》读书笔记18: 第四部分 控制流 - 迭代器、生成器和经典协程

作者: andylin02
学习章节: 第 17 章 迭代器、生成器和经典协程
关键词: 迭代器|可迭代对象|生成器|yield|生成器表达式|协程|send|throw|close|itertools|惰性求值|yield from


一、本章概述

第 17 章是《流畅的 Python》中关于“控制流程”部分的开始,深入探讨了 Python 中迭代器、生成器和经典协程(基于生成器的协程)的核心概念。这些特性是 Python 实现惰性求值、高效处理大数据流、以及构建协作式多任务的基础。

“迭代器是 yield 的起源,而 yield 又是协程的基石。” —— David Beazley

本章主要内容结构:

  • 可迭代对象与迭代器协议:理解 __iter__ 和 __next__ 方法,以及 for 循环背后的工作原理。
  • 生成器函数:使用 yield 关键字简化迭代器的创建,实现惰性产生值的序列。
  • 生成器表达式:简洁的惰性评估列表推导替代方案。
  • 标准库中的生成器工具:itertools 模块提供了大量用于处理生成器的迭代器构建块,如 islice、cycle、chain、takewhile 等。
  • 经典协程:生成器通过 .send()、.throw() 和 .close() 方法可以作为“协程”使用,实现数据的双向传递和协作式多任务。
  • 从迭代器到协程的演进:理解生成器如何从简单的迭代器演变为拥有独立执行状态的协程。

前置知识:第 12 章(序列协议)和第 14 章(迭代器协议介绍)有助于更好地理解本章。

二、迭代器与可迭代对象

2.1 概念区分

在 Python 中,理解“可迭代对象”和“迭代器”的区别至关重要。

概念定义示例特殊方法
可迭代对象 实现了 __iter__ 方法的对象,该方法返回一个迭代器。任何可以用于 for 循环的对象都是可迭代对象。 list, tuple, str, dict, file 等 __iter__
迭代器 实现了 __next__ 方法的对象,且 __iter__ 返回自身。迭代器是惰性的,一次产出一个值,直到 StopIteration。 生成器对象、iter(list) 返回的对象 __next__ 和 __iter__

关系:可迭代对象通过 iter() 获得迭代器;迭代器在 for 循环中不断调用 next() 获取值。

# 示例:迭代器协议
s = 'ABC' # s 是可迭代对象
it = iter(s) # 通过 iter() 获得迭代器
print(next(it)) # 'A'
print(next(it)) # 'B'
print(next(it)) # 'C'
# print(next(it)) # StopIteration 异常

2.2 实现可迭代对象

任何实现了 __iter__ 方法的类都是可迭代对象。通常 __iter__ 应该返回一个实现了 __next__ 的迭代器。

class Sentence:
def __init__(self, text):
self.text = text
self.words = text.split()

def __iter__(self):
# 返回一个迭代器(通常是生成器,但这里展示传统写法)
return SentenceIterator(self.words)

class SentenceIterator:
def __init__(self, words):
self.words = words
self.index = 0

def __next__(self):
try:
word = self.words[self.index]
except IndexError:
raise StopIteration()
self.index += 1
return word

def __iter__(self):
return self

s = Sentence('Hello world Python')
for word in s:
print(word) # Hello world Python

更 Pythonic 的方式是使用生成器函数作为 __iter__ 方法(见下一节)。

2.3 迭代器协议流程图

#mermaid-svg-TJb1epWx70nQ6Y92{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-TJb1epWx70nQ6Y92 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-TJb1epWx70nQ6Y92 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-TJb1epWx70nQ6Y92 .error-icon{fill:#552222;}#mermaid-svg-TJb1epWx70nQ6Y92 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-TJb1epWx70nQ6Y92 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-TJb1epWx70nQ6Y92 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-TJb1epWx70nQ6Y92 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-TJb1epWx70nQ6Y92 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-TJb1epWx70nQ6Y92 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-TJb1epWx70nQ6Y92 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-TJb1epWx70nQ6Y92 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-TJb1epWx70nQ6Y92 .marker.cross{stroke:#333333;}#mermaid-svg-TJb1epWx70nQ6Y92 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-TJb1epWx70nQ6Y92 p{margin:0;}#mermaid-svg-TJb1epWx70nQ6Y92 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-TJb1epWx70nQ6Y92 .cluster-label text{fill:#333;}#mermaid-svg-TJb1epWx70nQ6Y92 .cluster-label span{color:#333;}#mermaid-svg-TJb1epWx70nQ6Y92 .cluster-label span p{background-color:transparent;}#mermaid-svg-TJb1epWx70nQ6Y92 .label text,#mermaid-svg-TJb1epWx70nQ6Y92 span{fill:#333;color:#333;}#mermaid-svg-TJb1epWx70nQ6Y92 .node rect,#mermaid-svg-TJb1epWx70nQ6Y92 .node circle,#mermaid-svg-TJb1epWx70nQ6Y92 .node ellipse,#mermaid-svg-TJb1epWx70nQ6Y92 .node polygon,#mermaid-svg-TJb1epWx70nQ6Y92 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-TJb1epWx70nQ6Y92 .rough-node .label text,#mermaid-svg-TJb1epWx70nQ6Y92 .node .label text,#mermaid-svg-TJb1epWx70nQ6Y92 .image-shape .label,#mermaid-svg-TJb1epWx70nQ6Y92 .icon-shape .label{text-anchor:middle;}#mermaid-svg-TJb1epWx70nQ6Y92 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-TJb1epWx70nQ6Y92 .rough-node .label,#mermaid-svg-TJb1epWx70nQ6Y92 .node .label,#mermaid-svg-TJb1epWx70nQ6Y92 .image-shape .label,#mermaid-svg-TJb1epWx70nQ6Y92 .icon-shape .label{text-align:center;}#mermaid-svg-TJb1epWx70nQ6Y92 .node.clickable{cursor:pointer;}#mermaid-svg-TJb1epWx70nQ6Y92 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-TJb1epWx70nQ6Y92 .arrowheadPath{fill:#333333;}#mermaid-svg-TJb1epWx70nQ6Y92 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-TJb1epWx70nQ6Y92 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-TJb1epWx70nQ6Y92 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TJb1epWx70nQ6Y92 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-TJb1epWx70nQ6Y92 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TJb1epWx70nQ6Y92 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-TJb1epWx70nQ6Y92 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-TJb1epWx70nQ6Y92 .cluster text{fill:#333;}#mermaid-svg-TJb1epWx70nQ6Y92 .cluster span{color:#333;}#mermaid-svg-TJb1epWx70nQ6Y92 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-TJb1epWx70nQ6Y92 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-TJb1epWx70nQ6Y92 rect.text{fill:none;stroke-width:0;}#mermaid-svg-TJb1epWx70nQ6Y92 .icon-shape,#mermaid-svg-TJb1epWx70nQ6Y92 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-TJb1epWx70nQ6Y92 .icon-shape p,#mermaid-svg-TJb1epWx70nQ6Y92 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-TJb1epWx70nQ6Y92 .icon-shape .label rect,#mermaid-svg-TJb1epWx70nQ6Y92 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-TJb1epWx70nQ6Y92 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-TJb1epWx70nQ6Y92 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-TJb1epWx70nQ6Y92 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

iter()

for 循环使用

for 循环

调用 iter(obj) 获取迭代器

反复调用 next(iterator)

捕获 StopIteration 结束

迭代器

__next__ → 返回下一个值

__iter__ → 返回自身

可迭代对象

__iter__ → 返回迭代器

三、生成器函数

生成器函数是包含 yield 关键字的函数。调用生成器函数时,它返回一个生成器对象(该对象是迭代器的子类型),不会立即执行函数体。每次调用 next() 时,函数执行到下一个 yield 并暂停,返回 yield 的值。当函数返回(或执行完)时,抛出 StopIteration。

3.1 基本生成器

def gen_123():
print('start')
yield 1
print('continue')
yield 2
print('end')
yield 3

g = gen_123()
print(next(g)) # start \\n 1
print(next(g)) # continue \\n 2
print(next(g)) # end \\n 3
# print(next(g)) # StopIteration

3.2 生成器作为迭代器的高级用法

生成器自动实现迭代器协议,因此非常适用于实现可迭代对象。

class Sentence:
def __init__(self, text):
self.text = text
self.words = text.split()

def __iter__(self):
for word in self.words:
yield word # 生成器函数作为 __iter__

s = Sentence('Hello world')
for w in s:
print(w)

3.3 惰性生成器:处理大数据文件

def read_large_file(file_path):
with open(file_path) as f:
for line in f:
yield line.strip()

for line in read_large_file('bigfile.txt'):
print(line)

四、生成器表达式

生成器表达式语法与列表推导类似,但使用圆括号而不是方括号。它惰性求值,每次只产出一个元素,适合处理大数据流。

# 列表推导:立即生成全部元素
l = [x*2 for x in range(10)] # 列表
# 生成器表达式:返回生成器对象
g = (x*2 for x in range(10)) # 生成器

print(next(g)) # 0
print(next(g)) # 2
# 可以传递给函数,无需额外括号
sum_of_squares = sum(x*x for x in range(1000000)) # 高效,不产生完整列表

生成器表达式 vs 列表推导对比图

#mermaid-svg-8IEAUzML9Y0CzOMv{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8IEAUzML9Y0CzOMv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8IEAUzML9Y0CzOMv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8IEAUzML9Y0CzOMv .error-icon{fill:#552222;}#mermaid-svg-8IEAUzML9Y0CzOMv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8IEAUzML9Y0CzOMv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8IEAUzML9Y0CzOMv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8IEAUzML9Y0CzOMv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8IEAUzML9Y0CzOMv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8IEAUzML9Y0CzOMv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8IEAUzML9Y0CzOMv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8IEAUzML9Y0CzOMv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8IEAUzML9Y0CzOMv .marker.cross{stroke:#333333;}#mermaid-svg-8IEAUzML9Y0CzOMv svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8IEAUzML9Y0CzOMv p{margin:0;}#mermaid-svg-8IEAUzML9Y0CzOMv .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-8IEAUzML9Y0CzOMv .cluster-label text{fill:#333;}#mermaid-svg-8IEAUzML9Y0CzOMv .cluster-label span{color:#333;}#mermaid-svg-8IEAUzML9Y0CzOMv .cluster-label span p{background-color:transparent;}#mermaid-svg-8IEAUzML9Y0CzOMv .label text,#mermaid-svg-8IEAUzML9Y0CzOMv span{fill:#333;color:#333;}#mermaid-svg-8IEAUzML9Y0CzOMv .node rect,#mermaid-svg-8IEAUzML9Y0CzOMv .node circle,#mermaid-svg-8IEAUzML9Y0CzOMv .node ellipse,#mermaid-svg-8IEAUzML9Y0CzOMv .node polygon,#mermaid-svg-8IEAUzML9Y0CzOMv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8IEAUzML9Y0CzOMv .rough-node .label text,#mermaid-svg-8IEAUzML9Y0CzOMv .node .label text,#mermaid-svg-8IEAUzML9Y0CzOMv .image-shape .label,#mermaid-svg-8IEAUzML9Y0CzOMv .icon-shape .label{text-anchor:middle;}#mermaid-svg-8IEAUzML9Y0CzOMv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8IEAUzML9Y0CzOMv .rough-node .label,#mermaid-svg-8IEAUzML9Y0CzOMv .node .label,#mermaid-svg-8IEAUzML9Y0CzOMv .image-shape .label,#mermaid-svg-8IEAUzML9Y0CzOMv .icon-shape .label{text-align:center;}#mermaid-svg-8IEAUzML9Y0CzOMv .node.clickable{cursor:pointer;}#mermaid-svg-8IEAUzML9Y0CzOMv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8IEAUzML9Y0CzOMv .arrowheadPath{fill:#333333;}#mermaid-svg-8IEAUzML9Y0CzOMv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8IEAUzML9Y0CzOMv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8IEAUzML9Y0CzOMv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8IEAUzML9Y0CzOMv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8IEAUzML9Y0CzOMv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8IEAUzML9Y0CzOMv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8IEAUzML9Y0CzOMv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8IEAUzML9Y0CzOMv .cluster text{fill:#333;}#mermaid-svg-8IEAUzML9Y0CzOMv .cluster span{color:#333;}#mermaid-svg-8IEAUzML9Y0CzOMv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8IEAUzML9Y0CzOMv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8IEAUzML9Y0CzOMv rect.text{fill:none;stroke-width:0;}#mermaid-svg-8IEAUzML9Y0CzOMv .icon-shape,#mermaid-svg-8IEAUzML9Y0CzOMv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8IEAUzML9Y0CzOMv .icon-shape p,#mermaid-svg-8IEAUzML9Y0CzOMv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8IEAUzML9Y0CzOMv .icon-shape .label rect,#mermaid-svg-8IEAUzML9Y0CzOMv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8IEAUzML9Y0CzOMv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8IEAUzML9Y0CzOMv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8IEAUzML9Y0CzOMv :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

生成器表达式

返回生成器对象

惰性求值,逐个产出

只能迭代一次

列表推导

构建整个列表

占用内存

可重复使用

五、标准库中的生成器工具:itertools

itertools 模块提供了许多用于操作迭代器的生成器函数。以下是几种常用类型:

5.1 无限迭代器

函数说明示例
count(start, step) 从 start 开始无限递增 count(10, 2) → 10, 12, 14, …
cycle(iterable) 无限重复迭代序列 cycle('AB') → A, B, A, B, …
repeat(elem, n) 重复元素 n 次(默认无限) repeat(7, 3) → 7, 7, 7

5.2 有限迭代器(组合、选择)

函数说明示例
chain(*iters) 将多个可迭代对象串联 chain('AB', [1,2]) → A, B, 1, 2
zip_longest(*iters, fillvalue) 类似 zip,但用 fillvalue 填充短边 zip_longest('AB', '123', fillvalue='?') → (A,1), (B,2), (?,3)
islice(iter, start, stop, step) 切片迭代器 islice(count(0), 5) → 0,1,2,3,4
takewhile(predicate, iter) 满足条件时产出,一旦不满足停止 takewhile(lambda x: x<5, count(0)) → 0,1,2,3,4

5.3 组合生成器

函数说明示例
product(*iters, repeat=1) 笛卡尔积 product('AB', repeat=2) → AA, AB, BA, BB
permutations(iter, r) 长度为 r 的排列 permutations('ABC', 2) → AB, AC, BA, BC, CA, CB
combinations(iter, r) 按顺序的组合 combinations('ABC', 2) → AB, AC, BC

import itertools

# 生成前 10 个偶数
evens = list(itertools.islice(itertools.count(0, 2), 10))
print(evens) # [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

# 笛卡尔积
colors = ['red', 'green']
sizes = ['S', 'M']
product = list(itertools.product(colors, sizes))
print(product) # [('red', 'S'), ('red', 'M'), ('green', 'S'), ('green', 'M')]

六、经典协程:生成器的进阶

Python 2.5 为生成器增加了 .send(value)、.throw(type, value, tb) 和 .close() 方法,使得生成器不仅可以产出值,还可以接收外部传入的值,从而成为“协程”。

协程与生成器的区别:

  • 生成器:产生值给调用者。调用者使用 next() 或 for 循环获取值。
  • 协程:消耗(接收)值,同时也可以产出值。调用者使用 .send(value) 将值发送给协程,协程用 yield 接收。

6.1 协程的状态

协程有四种状态,可以通过 inspect.getgeneratorstate(gen) 查看:

  • GEN_CREATED:等待开始执行
  • GEN_RUNNING:正在执行(通常只在多线程中看到)
  • GEN_SUSPENDED:在 yield 表达式处暂停
  • GEN_CLOSED:执行结束或已关闭

6.2 一个简单的协程:累加器

def averager():
total = 0.0
count = 0
average = None
while True:
term = yield average # 接收值,产出平均值
total += term
count += 1
average = total / count

coro = averager()
next(coro) # 预激协程,执行到 yield,产出 None
print(coro.send(10)) # 10.0
print(coro.send(20)) # 15.0
print(coro.send(30)) # 20.0
coro.close() # 关闭协程

重要:协程必须通过 next(coro) 或 coro.send(None) 预激,使其前进到第一个 yield 表达式。否则无法发送值。

6.3 使用装饰器预激协程

from functools import wraps

def coroutine(func):
@wraps(func)
def primer(*args, **kwargs):
gen = func(*args, **kwargs)
next(gen) # 预激
return gen
return primer

@coroutine
def averager():
total = 0.0
count = 0
avg = None
while True:
term = yield avg
total += term
count += 1
avg = total / count

coro = averager() # 自动预激
print(coro.send(10)) # 10.0

6.4 协程的异常处理与关闭

  • generator.throw(exc_type[, exc_value[, traceback]]):在协程的当前 yield 处抛出异常。如果协程捕获并处理了异常,可以继续运行;否则协程终止。
  • generator.close():在协程的当前 yield 处抛出 GeneratorExit 异常。协程应捕获该异常以执行清理(通常不需要特殊处理),若不处理,协程直接终止。

@coroutine
def averager():
total = 0
count = 0
avg = None
try:
while True:
term = yield avg
total += term
count += 1
avg = total / count
except GeneratorExit:
print('协程关闭')
except ValueError:
print('收到无效值,跳过')

coro = averager()
coro.send(10)
coro.throw(ValueError, 'bad data') # 输出"收到无效值,跳过",协程继续
coro.send(20) # 正常
coro.close() # 输出"协程关闭"

七、从生成器到协程的演进图

#mermaid-svg-gflRcCRH9fIsFQeo{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gflRcCRH9fIsFQeo .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gflRcCRH9fIsFQeo .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gflRcCRH9fIsFQeo .error-icon{fill:#552222;}#mermaid-svg-gflRcCRH9fIsFQeo .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gflRcCRH9fIsFQeo .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gflRcCRH9fIsFQeo .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gflRcCRH9fIsFQeo .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gflRcCRH9fIsFQeo .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gflRcCRH9fIsFQeo .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gflRcCRH9fIsFQeo .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gflRcCRH9fIsFQeo .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gflRcCRH9fIsFQeo .marker.cross{stroke:#333333;}#mermaid-svg-gflRcCRH9fIsFQeo svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gflRcCRH9fIsFQeo p{margin:0;}#mermaid-svg-gflRcCRH9fIsFQeo defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-gflRcCRH9fIsFQeo g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-gflRcCRH9fIsFQeo g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-gflRcCRH9fIsFQeo g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-gflRcCRH9fIsFQeo g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-gflRcCRH9fIsFQeo g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-gflRcCRH9fIsFQeo .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-gflRcCRH9fIsFQeo .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-gflRcCRH9fIsFQeo .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-gflRcCRH9fIsFQeo .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-gflRcCRH9fIsFQeo .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-gflRcCRH9fIsFQeo .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-gflRcCRH9fIsFQeo .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-gflRcCRH9fIsFQeo .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gflRcCRH9fIsFQeo .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gflRcCRH9fIsFQeo .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gflRcCRH9fIsFQeo .edgeLabel .label text{fill:#333;}#mermaid-svg-gflRcCRH9fIsFQeo .label div .edgeLabel{color:#333;}#mermaid-svg-gflRcCRH9fIsFQeo .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-gflRcCRH9fIsFQeo .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-gflRcCRH9fIsFQeo .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-gflRcCRH9fIsFQeo .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-gflRcCRH9fIsFQeo .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-gflRcCRH9fIsFQeo .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gflRcCRH9fIsFQeo .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gflRcCRH9fIsFQeo #statediagram-barbEnd{fill:#333333;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gflRcCRH9fIsFQeo .cluster-label,#mermaid-svg-gflRcCRH9fIsFQeo .nodeLabel{color:#131300;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-gflRcCRH9fIsFQeo .note-edge{stroke-dasharray:5;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-note text{fill:black;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram-note .nodeLabel{color:black;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagram .edgeLabel{color:red;}#mermaid-svg-gflRcCRH9fIsFQeo #dependencyStart,#mermaid-svg-gflRcCRH9fIsFQeo #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-gflRcCRH9fIsFQeo .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gflRcCRH9fIsFQeo :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

调用生成器函数

next(g)

next(g) / send(v)

执行完毕 / throw / close

GEN_CREATED

GEN_SUSPENDED

GEN_CLOSED

八、综合案例:带协程的实时平均数计算

def running_average():
total = 0
count = 0
avg = 0
while True:
value = yield avg
if value is None: # 允许用 send(None) 退出循环
break
total += value
count += 1
avg = total / count
return (total, count, avg) # 协程的返回值会附加到 StopIteration 异常上

def use_avg():
coro = running_average()
next(coro)
print(coro.send(10))
print(coro.send(20))
print(coro.send(30))
try:
coro.send(None)
except StopIteration as e:
result = e.value
print(f"Total: {result[0]}, Count: {result[1]}, Average: {result[2]}")

use_avg()

九、经典协程与 yield from

本章节仅简要提及 yield from,因为《流畅的 Python》第 2 版中,经典协程(基于生成器的协程)和原生协程(async/await)分属不同章节。yield from 将在下一章详细说明。

yield from 可以将迭代任务委托给另一个生成器,简化协程中嵌套子协程的代码,同时支持双向数据传递和异常传播。

十、本章思维导图

#mermaid-svg-7ja9jk6AkJnNzpk3{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7ja9jk6AkJnNzpk3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7ja9jk6AkJnNzpk3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7ja9jk6AkJnNzpk3 .error-icon{fill:#552222;}#mermaid-svg-7ja9jk6AkJnNzpk3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7ja9jk6AkJnNzpk3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7ja9jk6AkJnNzpk3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7ja9jk6AkJnNzpk3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7ja9jk6AkJnNzpk3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7ja9jk6AkJnNzpk3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7ja9jk6AkJnNzpk3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7ja9jk6AkJnNzpk3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7ja9jk6AkJnNzpk3 .marker.cross{stroke:#333333;}#mermaid-svg-7ja9jk6AkJnNzpk3 svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7ja9jk6AkJnNzpk3 p{margin:0;}#mermaid-svg-7ja9jk6AkJnNzpk3 .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-7ja9jk6AkJnNzpk3 .cluster-label text{fill:#333;}#mermaid-svg-7ja9jk6AkJnNzpk3 .cluster-label span{color:#333;}#mermaid-svg-7ja9jk6AkJnNzpk3 .cluster-label span p{background-color:transparent;}#mermaid-svg-7ja9jk6AkJnNzpk3 .label text,#mermaid-svg-7ja9jk6AkJnNzpk3 span{fill:#333;color:#333;}#mermaid-svg-7ja9jk6AkJnNzpk3 .node rect,#mermaid-svg-7ja9jk6AkJnNzpk3 .node circle,#mermaid-svg-7ja9jk6AkJnNzpk3 .node ellipse,#mermaid-svg-7ja9jk6AkJnNzpk3 .node polygon,#mermaid-svg-7ja9jk6AkJnNzpk3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-7ja9jk6AkJnNzpk3 .rough-node .label text,#mermaid-svg-7ja9jk6AkJnNzpk3 .node .label text,#mermaid-svg-7ja9jk6AkJnNzpk3 .image-shape .label,#mermaid-svg-7ja9jk6AkJnNzpk3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-7ja9jk6AkJnNzpk3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-7ja9jk6AkJnNzpk3 .rough-node .label,#mermaid-svg-7ja9jk6AkJnNzpk3 .node .label,#mermaid-svg-7ja9jk6AkJnNzpk3 .image-shape .label,#mermaid-svg-7ja9jk6AkJnNzpk3 .icon-shape .label{text-align:center;}#mermaid-svg-7ja9jk6AkJnNzpk3 .node.clickable{cursor:pointer;}#mermaid-svg-7ja9jk6AkJnNzpk3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-7ja9jk6AkJnNzpk3 .arrowheadPath{fill:#333333;}#mermaid-svg-7ja9jk6AkJnNzpk3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-7ja9jk6AkJnNzpk3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-7ja9jk6AkJnNzpk3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7ja9jk6AkJnNzpk3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-7ja9jk6AkJnNzpk3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7ja9jk6AkJnNzpk3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-7ja9jk6AkJnNzpk3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-7ja9jk6AkJnNzpk3 .cluster text{fill:#333;}#mermaid-svg-7ja9jk6AkJnNzpk3 .cluster span{color:#333;}#mermaid-svg-7ja9jk6AkJnNzpk3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-7ja9jk6AkJnNzpk3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7ja9jk6AkJnNzpk3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-7ja9jk6AkJnNzpk3 .icon-shape,#mermaid-svg-7ja9jk6AkJnNzpk3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-7ja9jk6AkJnNzpk3 .icon-shape p,#mermaid-svg-7ja9jk6AkJnNzpk3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-7ja9jk6AkJnNzpk3 .icon-shape .label rect,#mermaid-svg-7ja9jk6AkJnNzpk3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-7ja9jk6AkJnNzpk3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-7ja9jk6AkJnNzpk3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-7ja9jk6AkJnNzpk3 :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

第17章 迭代器、生成器和经典协程

可迭代对象 vs 迭代器

__iter__ 返回迭代器

__next__ + __iter__ 返回自身

生成器函数

yield 关键字

惰性求值

函数状态暂停/恢复

生成器表达式

圆括号语法

逐个产出,节省内存

itertools 模块

count, cycle, repeat

product, permutations, combinations

islice, takewhile, chain

经典协程

.send(value) 发送数据

.throw(exc) 注入异常

.close() 关闭协程

预激: next(c) 或 send(None)

GEN_CREATED, SUSPENDED, CLOSED

yield from 简述

委托给子生成器

十一、常见错误与最佳实践

错误原因解决方案
迭代时修改被迭代的集合 迭代器持有的索引失效 迭代前复制一份,如 list(items) 或使用切片
忘记预激协程 协程未执行到第一个 yield,send 会失败 使用装饰器自动预激,或显式调用 next(coro)
在生成器中抛出 StopIteration 早期 Python 版本允许,3.5+ 会引发 RuntimeError 使用 return 返回值(附加到异常对象)
对迭代器重复迭代 迭代器只能使用一次 需要重新迭代时,从可迭代对象重新获取迭代器
生成器表达式需要加括号 作为唯一参数时可省略,否则必须加括号 明确 sum(x for x in …) 不需要,但 f(1, x for x in …) 错误

最佳实践:

  • 优先使用生成器函数和生成器表达式代替手写迭代器类。
  • 处理大型数据流时,始终使用生成器/迭代器,避免构建完整的中间列表。
  • 对于协程,使用 @coroutine 装饰器自动预激,避免遗忘导致奇怪错误。
  • 使用 itertools 模块的丰富函数,避免自己实现复杂的迭代逻辑。
  • 理解协程的四种状态,调试时通过 inspect.getgeneratorstate() 检查。

十二、本章总结

第 17 章是 Python 异步编程和控制流程的重要基石,系统讲解了:

  • 迭代器与可迭代对象:通过实现 __iter__ 和 __next__ 可以创建自己的序列类型。
  • 生成器函数与表达式:使用 yield 替代 __next__ 和 __iter__ 的样板代码,是 Python 中最简洁优雅的迭代器实现方式。
  • itertools 模块:提供了标准的高阶迭代器工具,能大幅简化数据处理代码。
  • 经典协程:生成器扩展了 .send() 方法后变为协程,允许双向通信。协程可以维护状态,适合实现数据流处理管道。
  • 协程的生命周期:预激 → 发送值 → 可能异常 → 关闭。

掌握了本章内容,你就能够编写高效、惰性、可组合的数据处理代码,并为学习基于 async/await 的原生协程打下坚实基础。

十三、思考题

  • 一个可迭代对象和一个迭代器之间的根本区别是什么?for 循环如何处理二者?
  • 为什么生成器函数比实现 __iter__ 和 __next__ 的类更受欢迎?
  • 使用 yield from 相比于手动 for 循环处理子生成器有什么优势?(可提前预习)
  • 协程的 .send() 方法和普通生成器的 next() 方法在使生成器恢复执行时有何不同?
  • 如何安全地让协程终止并获取其最终返回值?
  • 十四、下一章预告

    第 18 章《使用 yield from》

    在第 17 章学习了生成器作为协程的基础后,第 18 章将深入 yield from 这个关键语法。yield from 不仅简化了在生成器中委托给子生成器的代码,还能双向传递数据、处理异常、获取子生成器的返回值。它是将多个协程组合成管道或复杂流程的核心构造。

    你将学到:

    • yield from 的基本语法与行为
    • 如何用 yield from 重构迭代器代码
    • 用 yield from 编写协程委派器(delegating generator)
    • 子生成器的返回值获取
    • yield from 与异常传播
    • 应用案例:使用 yield from 实现一个简单的任务调度器

    第 18 章将为你打开高效构建协作式多任务系统的大门,并为后续学习 asyncio 打下坚实基础。


    本文为个人学习笔记,仅用于知识分享。如有错误,欢迎指正。
    👍🏻 点赞 + 收藏 + 分享,让更多开发者看到这篇深度解析!❤️ 如果觉得有用,请给个赞支持一下作者!

    赞(0)
    未经允许不得转载:171主机测评 » 《流畅的Python》读书笔记18: 第四部分 控制流 - 迭代器、生成器和经典协程
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址