生成器 / 迭代器的区别

一句话速记

迭代器(Iterator)= 实现了 __iter__ + __next__ 的对象,每次调用 next() 返回下一个值,用完抛 StopIteration生成器(Generator)= 用 yield 定义的函数,是迭代器的一种特殊实现——调用时不执行函数体,而是返回一个 Generator 对象(已是迭代器)。生成器的核心优势:惰性求值(不一次性计算所有值)+ 保存执行状态yield 暂停,下次 next() 继续)。LLM 流式输出就是典型的生成器模式。

通俗解释(5 分钟版)

迭代器 vs 可迭代对象

# 可迭代对象(Iterable):有 __iter__ 方法,能 for 循环
my_list = [1, 2, 3]    # list 是可迭代对象
for x in my_list: ...  # 内部调用 iter(my_list) 得到迭代器
 
# 迭代器(Iterator):有 __iter__ 和 __next__,能 next()
my_iter = iter(my_list)   # list_iterator 对象
next(my_iter)  # 1
next(my_iter)  # 2
next(my_iter)  # 3
next(my_iter)  # StopIteration!
 
# 关键区别:
# - 可迭代对象可以多次 for 循环(每次创建新迭代器)
# - 迭代器只能遍历一次(状态不重置)

生成器 = 语法糖版迭代器

# 手写迭代器(繁琐)
class CountUp:
    def __init__(self, start, end):
        self.current = start
        self.end = end
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current >= self.end:
            raise StopIteration
        val = self.current
        self.current += 1
        return val
 
# 生成器版本(简洁)
def count_up(start, end):
    current = start
    while current < end:
        yield current    # 暂停,返回 current;下次 next() 从这里继续
        current += 1
 
# 使用完全一样:
for x in count_up(0, 5):
    print(x)  # 0 1 2 3 4

关键细节

1)yield 的执行流程

def gen():
    print("A")
    yield 1      # 第一次 next() 返回 1,暂停在这里
    print("B")
    yield 2      # 第二次 next() 返回 2,暂停在这里
    print("C")
    # 函数结束 → 自动 raise StopIteration
 
g = gen()       # 创建生成器对象,不执行任何代码!
print(next(g))  # 打印 "A",返回 1
print(next(g))  # 打印 "B",返回 2
print(next(g))  # 打印 "C",StopIteration

关键:调用 gen()不执行函数体,只返回生成器对象。每次 next() 才从上次 yield 的地方继续执行到下一个 yield

2)生成器表达式 vs 列表推导式

# 列表推导式:立即计算,全部存内存
squares_list = [x**2 for x in range(1000000)]   # 占用几十MB内存
 
# 生成器表达式:惰性计算,按需生成
squares_gen = (x**2 for x in range(1000000))    # 几乎不占内存
 
# 使用相同:
for s in squares_gen:
    process(s)
 
# 关键场景:处理大文件(不能一次性加载到内存)
def read_large_file(path):
    with open(path) as f:
        for line in f:
            yield line.strip()  # 每次只读一行到内存

3)yield from(委托生成器)

# 普通写法(繁琐)
def chain(iter1, iter2):
    for x in iter1:
        yield x
    for x in iter2:
        yield x
 
# yield from(简洁)
def chain(iter1, iter2):
    yield from iter1
    yield from iter2
 
# yield from 还处理了 send() 和 throw() 的转发
# 在协程(asyncio)里,await 就是基于 yield from 实现的

4)send() —— 双向生成器

def accumulator():
    total = 0
    while True:
        value = yield total    # yield 返回 total,同时接收 send() 发来的 value
        total += value
 
g = accumulator()
next(g)          # 启动生成器(必须先 next 到第一个 yield)
g.send(10)       # → total=10,yield 返回 10
g.send(20)       # → total=30,yield 返回 30
g.send(5)        # → total=35,yield 返回 35

send() 是 asyncio 协程的底层机制await 在底层就是 yield from,事件循环通过 send() 把 I/O 结果传回协程。

5)LLM 流式输出的生成器模式

# OpenAI SDK 的流式响应就是生成器
from openai import OpenAI
client = OpenAI()
 
def stream_completion(prompt: str):
    """生成器函数,逐 token 产出"""
    stream = client.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}],
        stream=True
    )
    for chunk in stream:
        if chunk.choices[0].delta.content:
            yield chunk.choices[0].delta.content  # 逐 token yield
 
# FastAPI 流式响应
from fastapi.responses import StreamingResponse
 
@app.post("/chat/stream")
async def chat_stream(request: ChatRequest):
    async def generator():
        for token in stream_completion(request.prompt):
            yield f"data: {token}\n\n"  # SSE 格式
    
    return StreamingResponse(generator(), media_type="text/event-stream")

6)迭代器协议 vs 异步迭代器

# 同步迭代器(__iter__ + __next__)
class SyncIter:
    def __iter__(self): return self
    def __next__(self): ...
 
# 异步迭代器(__aiter__ + __anext__)
class AsyncIter:
    def __aiter__(self): return self
    async def __anext__(self):
        data = await fetch_data()
        if not data: raise StopAsyncIteration
        return data
 
# 异步生成器(async def + yield)
async def async_stream():
    async for item in some_async_source():
        yield item  # 异步生成器
 
# 使用:
async for item in async_stream():
    process(item)

延伸追问

  • Q:for 循环和迭代器的关系是什么?for x in obj 等价于:先调用 iter(obj) 得到迭代器,然后反复调用 next(iterator) 直到 StopIteration。如果 obj 本身是迭代器,iter(obj) 返回自身(因为迭代器的 __iter__ 返回 self)。
  • Q:列表可以 for 多次,迭代器只能一次,为什么? → 列表每次 iter(list) 创建新的迭代器(内部有 index,从 0 开始);列表本身不记录遍历位置,所以可以重复遍历。迭代器记录了当前状态(__next__ 推进),遍历完就”耗尽”,iter(exhausted_iter) 返回自身(同一个耗尽的对象)。
  • Q:range(10) 是可迭代对象还是迭代器? → 是可迭代对象,不是迭代器。range(10) 是惰性序列(不存储所有数),每次 iter(range(10)) 返回新的 range_iterator,可以多次 for 循环。这和生成器不同——生成器只能遍历一次。

我的记法

  • 可迭代 = 有 __iter__(能 for 循环,可多次)
  • 迭代器 = 有 __iter__ + __next__(有状态,只能一次)
  • 生成器 = yield 函数 = 自动实现迭代器
  • yield 是暂停点:函数状态保存,下次 next() 从这里继续
  • 生成器优势:惰性求值(大数据/流式),不一次性加载到内存
  • LLM 流式输出 = 生成器模式(逐 token yield)
  • 一句话:「生成器是懒人版迭代器——yield 挂起状态,next() 唤醒继续」

状态

  • 已背速记
  • 能讲通俗版
  • 能写 LLM 流式输出的生成器代码