Skip to content

10 迭代器与生成器

📋 资料说明

  • 来源:课程 day09 后半 ~ day10 前半
  • 笔记尚硅谷大模型技术之Python1.0.docx 第 11 章
  • 每日一考day09_每日一考.mdday10_每日一考.md
  • 视频:❌ 跳过(约 8 个视频:迭代器介绍 → 自定义迭代器 → 生成器表达式 → 生成器函数 → send)

🟢 🤖 AI 替代率:90%

为什么是这个水平:

迭代器和生成器的语法 AI 可以完全生成。但以下概念需要人类理解:

  • 迭代器协议__iter____next__ 的调用链——人是靠理解协议才能写出正确的自定义迭代器,AI 只是"背"出了模式
  • yield 语义:生成器函数执行到 yield 时"冻结"状态,下次从 yield 后面继续执行——这个暂停/恢复机制对新手违反直觉
  • send 方法generator.send(value) 可以向生成器发送数据,恢复执行时 yield 表达式返回这个值——理解通道双向通信需要人类思考

👤 人工干预率:10% | 人类重点关注

为什么需要人工干预:

  1. 迭代器是一次性的:遍历完就不能再用,很多人第一次遇到 list(gen) 后再次遍历发现为空时会困惑
  2. 生成器是惰性计算:值在需要时才生成,不是一次性全部算出。这对内存优化有利,但调试时难以直接查看"所有值"
  3. send/throw/close 的方法族:高级用法,日常用到的机会不多,但了解后能在管道和协程场景中活用

人类与 AI 协作建议:

  1. 让 AI 写出迭代器和生成器的代码,你用 for 循环验证结果
  2. 把生成器表达式改成列表推导式运行一次,观察区别
  3. next() 单步调试生成器,理解"暂停→恢复"的流程

1. 迭代器

实现了 __iter____next__ 方法的对象就是迭代器。

python
# 可迭代对象(可以用 for 循环的)→ 实现了 __iter__()
# 迭代器(可以用 next() 的)→ 实现了 __iter__() 和 __next__()

# for 循环的本质:
my_list = [1, 2, 3]
# 1. 调用 iter(my_list) 获得迭代器
# 2. 反复调用 next(iterator) 获取下一个值
# 3. 遇到 StopIteration 退出

it = iter(my_list)       # 获取迭代器
print(next(it))  # 1
print(next(it))  # 2
print(next(it))  # 3
# print(next(it))  # StopIteration

自定义迭代器

python
class CountDown:
    """倒计时迭代器"""
    def __init__(self, start):
        self.current = start
    
    def __iter__(self):
        return self  # 迭代器返回自身
    
    def __next__(self):
        if self.current <= 0:
            raise StopIteration  # 结束
        value = self.current
        self.current -= 1
        return value

# 使用
for i in CountDown(5):
    print(i)  # 5, 4, 3, 2, 1

2. 生成器

生成器是简化版的迭代器——用 yield 关键字。

生成器函数

python
def count_down(n):
    """生成器函数——包含 yield 关键字"""
    while n > 0:
        yield n  # 返回 n,暂停执行
        n -= 1
    # 函数结束自动抛出 StopIteration

# 调用生成器函数返回生成器对象
gen = count_down(3)
print(next(gen))  # 3(第一次 yield)
print(next(gen))  # 2(从 yield 后面继续)
print(next(gen))  # 1
# print(next(gen))  # StopIteration

# 生成器可以用 for 循环
for i in count_down(3):
    print(i)

yield 的执行流程

python
def demo():
    print("1. 开始执行")
    yield "第一次暂停"
    print("2. 从这里继续")
    yield "第二次暂停"
    print("3. 结束")

gen = demo()
print(next(gen))  # → 1. 开始执行 → "第一次暂停"
print(next(gen))  # → 2. 从这里继续 → "第二次暂停"
print(next(gen))  # → 3. 结束 → StopIteration

生成器的应用

python
# 1. 无限序列(节省内存)
def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
for _ in range(10):
    print(next(fib), end=" ")  # 0 1 1 2 3 5 8 13 21 34

# 2. 读取大文件(逐行处理)
def read_large_file(filename):
    with open(filename, "r", encoding="utf-8") as f:
        for line in f:
            yield line.strip()

生成器表达式

python
# 语法类似列表推导式,但用 () 而不是 []
list_comp = [x**2 for x in range(10)]     # 立即计算,占用内存
gen_expr  = (x**2 for x in range(10))     # 惰性计算,节省内存

print(list_comp)  # [0, 1, 4, ..., 81]
print(gen_expr)   # <generator object <genexpr> at ...>

# 生成器表达式逐个取值
for val in gen_expr:
    print(val, end=" ")  # 0 1 4 9 16 25 36 49 64 81

# 生成器只可遍历一次
gen = (x for x in [1, 2, 3])
print(sum(gen))   # 6
print(sum(gen))   # 0(已经空了!)

3. send 方法

send() 可以向生成器发送数据,生成器内部通过 yield 表达式接收。

python
def echo():
    """回声生成器——接收数据并打印"""
    print("等待数据...")
    while True:
        received = yield  # yield 表达式接收 send 发送的值
        print(f"收到: {received}")

gen = echo()
next(gen)          # 启动生成器(执行到第一个 yield)→ "等待数据..."
gen.send("Hello")  # → "收到: Hello"
gen.send("World")  # → "收到: World"

# send 第一次启动需要传 None
# gen.send("start")  # ❌ TypeError: can't send non-None value to a just-started generator
gen.send(None)     # ✅ 等价于 next(gen)

获取 yield 的返回值

python
def accumulate():
    total = 0
    while True:
        value = yield total  # yield 返回当前 total,接收 send 的值
        total += value

acc = accumulate()
next(acc)           # 启动(yield 0)
print(acc.send(10)) # 10
print(acc.send(20)) # 30
print(acc.send(30)) # 60

练习题

练习 1:自定义迭代器

实现一个 Fibonacci 类迭代器,生成前 n 个斐波那契数。

练习 2:生成器平方数

编写生成器函数 squares(n),生成从 1 到 n 的平方数。

练习 3:文件读取生成器

用生成器逐行读取一个文件,过滤掉空行和注释行(以 # 开头)。


参考答案

练习 1:

python
class Fibonacci:
    def __init__(self, n):
        self.n = n
        self.a, self.b = 0, 1
        self.count = 0
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.count >= self.n:
            raise StopIteration
        value = self.a
        self.a, self.b = self.b, self.a + self.b
        self.count += 1
        return value

for f in Fibonacci(10):
    print(f, end=" ")  # 0 1 1 2 3 5 8 13 21 34

练习 2:

python
def squares(n):
    for i in range(1, n + 1):
        yield i ** 2

print(list(squares(5)))  # [1, 4, 9, 16, 25]

练习 3:

python
def read_filtered_lines(filename):
    with open(filename, "r", encoding="utf-8") as f:
        for line in f:
            stripped = line.strip()
            if stripped and not stripped.startswith("#"):
                yield stripped

OPC 超级个体实战指南