Python 迭代器与生成器:从协议到实战,一篇讲透 迭代器Iterator和生成器Generator是 Python 中两个紧密相关但常被混淆的概念。很多人会用for循环但对它们背后的工作机制却模糊不清。这篇文章从协议规范到内存模型再到实战场景把迭代器和生成器彻底讲清楚。一、迭代器协议Python 的 for 循环到底怎么工作的在 Python 中可迭代对象Iterable和迭代器Iterator是两个不同的概念可迭代对象实现了__iter__()方法返回一个迭代器迭代器实现了__iter__()和__next__()两个方法pythonfrom typing import Iterator, Iterable class MyRange: 自定义可迭代对象 def __init__(self, start: int, end: int): self.start start self.end end def __iter__(self) - Iterator[int]: return MyRangeIterator(self.start, self.end) class MyRangeIterator: 自定义迭代器 def __init__(self, current: int, end: int): self.current current self.end end def __iter__(self): return self def __next__(self) - int: if self.current self.end: raise StopIteration value self.current self.current 1 return value # 使用 for i in MyRange(0, 5): print(i) # 0, 1, 2, 3, 4for循环的背后python# Python 执行 for i in obj 时实际做了这些事 iterator iter(obj) # 调用 obj.__iter__() while True: try: i next(iterator) # 调用 iterator.__next__() except StopIteration: break # 迭代结束 # 执行循环体...关键理解可迭代对象是数据容器如 list、dict、set迭代器是数据游标记录当前遍历位置每次for循环都会通过iter()获取一个新的迭代器二、生成器用函数写迭代器生成器是用函数语法写迭代器的快捷方式。只要函数里用了yield它就变成生成器函数调用时返回生成器对象一种特殊的迭代器。pythondef my_range(start: int, end: int): 生成器版本的范围函数 current start while current end: yield current current 1 # 调用返回生成器对象 gen my_range(0, 5) print(type(gen)) # class generator for i in gen: print(i) # 0, 1, 2, 3, 4代码量对比方案代码行数需要维护的状态手写迭代器类~15 行__iter__、__next__、StopIteration生成器函数~5 行只需yield生成器的优势写法简洁不需要定义类和实现协议方法状态自动保存yield会保存当前局部变量、指令指针、调用栈惰性求值数据按需生成不一次性加载到内存三、生成器的执行模型yield 到底做了什么理解yield的执行流程是关键pythondef demo(): print(1. 开始执行) yield A print(2. yield 后继续) yield B print(3. 函数结束) gen demo() print(next(gen)) # 打印 1. 开始执行 → 返回 A print(next(gen)) # 从 yield A 后继续 → 打印 2. yield 后继续 → 返回 B print(next(gen)) # 从 yield B 后继续 → 打印 3. 函数结束 → 抛出 StopIteration执行流程text调用 demo() → 返回生成器对象函数体还未执行 第一次 next(gen) → 执行到 yield A → 暂停并返回 A 第二次 next(gen) → 从暂停位置继续 → 执行到 yield B → 暂停并返回 B 第三次 next(gen) → 从暂停位置继续 → 执行完毕 → 抛出 StopIteration关键理解yield既返回值又暂停执行。生成器函数的执行权在调用方和生成器之间交替。四、生成器的进阶用法send、throw、close4.1send()向生成器发送值send(value)可以向生成器传入一个值这个值会成为当前yield表达式的返回值。pythondef accumulator(): total 0 while True: value yield total # yield 返回 total同时接收外部传入的值 if value is None: break total value acc accumulator() next(acc) # 启动生成器执行到 yield total返回 0 print(acc.send(10)) # 传入 10total10执行到 yield total返回 10 print(acc.send(20)) # 传入 20total30执行到 yield total返回 30 acc.close() # 关闭生成器next(gen)等价于gen.send(None)所以必须先next()启动。实战场景协程、数据管道、交互式处理。4.2throw()向生成器抛出异常pythondef safe_processor(): try: while True: value yield print(f处理: {value}) except ValueError: print(收到 ValueError跳过当前数据) except GeneratorExit: print(生成器被关闭) gen safe_processor() next(gen) # 启动 gen.send(10) # 处理: 10 gen.throw(ValueError) # 收到 ValueError跳过当前数据 gen.send(20) # 处理: 20生成器继续工作 gen.close() # 生成器被关闭4.3close()提前终止生成器pythondef infinite_sequence(): n 0 try: while True: yield n n 1 except GeneratorExit: print(生成器被关闭清理资源...) gen infinite_sequence() print(next(gen)) # 0 print(next(gen)) # 1 gen.close() # 生成器被关闭清理资源... # next(gen) # 再调用会抛出 StopIteration最佳实践如果生成器持有文件句柄、网络连接等资源用try/finally或捕获GeneratorExit做清理。五、生成器表达式惰性版的列表推导式python# 列表推导式立即计算占用内存 squares_list [x**2 for x in range(10000000)] # 立即创建 1000 万个元素的列表 # 生成器表达式惰性计算不占内存 squares_gen (x**2 for x in range(10000000)) # 只返回生成器对象不计算 # 按需取值 print(next(squares_gen)) # 0 print(next(squares_gen)) # 1语法区别写法类型内存占用是否可复用[x**2 for x in range(10)]list高是(x**2 for x in range(10))generator低否注意生成器是一次性的。迭代完后再遍历不会产生任何值pythongen (x for x in range(3)) print(list(gen)) # [0, 1, 2] print(list(gen)) # [] 第二次为空六、实战场景生成器解决真实问题场景一处理超大日志文件pythondef tail_log(filepath: str, keyword: str): 实时跟踪日志文件过滤包含关键词的行 with open(filepath, r, encodingutf-8) as f: # 跳到文件末尾 f.seek(0, 2) while True: line f.readline() if not line: import time time.sleep(0.1) continue if keyword in line: yield line.strip() # 使用 for line in tail_log(/var/log/app.log, ERROR): print(f发现异常: {line}) if FATAL in line: break # 可随时停止场景二分页拉取 API 数据带限流pythonimport time import requests from typing import Generator, Dict, Any def paginated_api(base_url: str, page_size: int 50, rate_limit: float 1.0) - Generator[Dict[str, Any], None, None]: 分页拉取 API 数据支持限流 page 1 while True: resp requests.get( base_url, params{page: page, size: page_size}, timeout10 ) resp.raise_for_status() data resp.json() if not data.get(items): break for item in data[items]: yield item # 检查是否最后一页 if page data.get(total_pages, float(inf)): break page 1 time.sleep(rate_limit) # 限流 # 使用方完全不用关心页码和限流 for user in paginated_api(https://api.example.com/users): print(user[name]) if user[id] 1000: break场景三管道式数据处理pythondef read_numbers(n: int): for i in range(n): yield i def even_only(source): for num in source: if num % 2 0: yield num def square(source): for num in source: yield num * num def take(source, n: int): count 0 for item in source: if count n: break yield item count 1 # 链式组合取 100 以内偶数的平方取前 5 个 pipeline take(square(even_only(read_numbers(100))), 5) print(list(pipeline)) # [0, 4, 16, 36, 64]每个环节只做一件事数据惰性流动没有中间列表产生。七、迭代器 vs 生成器一句话对比对比维度迭代器生成器定义方式实现__iter____next__函数中有yield代码量较多需定义类较少函数即可状态管理手动维护实例变量自动保存局部变量可复用性可多次迭代重新创建实例一次性双向通信不支持支持send()/throw()适用场景需要封装复杂迭代逻辑大部分迭代场景八、记忆口诀迭代器是协议生成器是语法糖yield 暂停又返回next 驱动向前闯惰性求值省内存管道组合威力强。总结迭代器是实现了__iter__和__next__的对象是for循环的基础生成器是用yield写迭代器的快捷方式自动保存状态生成器表达式是惰性版的列表推导式节省内存send()、throw()、close()是生成器的进阶控制能力处理大文件、流式数据、管道组合时优先考虑生成器本文为纯技术分享不涉及任何品牌或产品。