迭代器和生成器
关于迭代器(Iterator)和生成器(Generator)的详细介绍和比较:
一、迭代器(Iterator)
1. 定义
- 迭代器(Iterator) 是 Python 中用于遍历(或迭代)集合(如列表、元组、字典、集合等)元素的一种机制。
- 它是一个可以记住遍历位置的对象,允许你逐个访问集合中的元素,而无需提前知道集合的大小。
2. 迭代器的特点
- 惰性计算:迭代器不会一次性加载所有数据到内存,而是按需生成元素,适合处理大数据集。
- 单向遍历:迭代器只能向前移动,不能回退或重置。
- 只能遍历一次:遍历结束后,迭代器会耗尽,再次遍历需要重新创建。
3. 核心方法(每个迭代器必不可少的迭代器协议)
- 要让一个类称为迭代器,必须实现
__iter__()
和__next__()
两种方法 __iter__()
:返回迭代器对象自身(self)(必须实现),在函数外创建迭代器可使用iter()
。__next__()
:返回下一个元素,若没有元素则抛出StopIteration
异常(必须实现)。在函数外使用迭代器调用next()
,即可调用一次迭代器生成的元素。
4. 自定义迭代器示例
class MyIterator:def __init__(self, max_num):self.max_num = max_numself.current = 0def __iter__(self):return selfdef __next__(self):if self.current < self.max_num:self.current += 1return self.currentelse:raise StopIteration# 使用自定义的迭代器
it = MyIterator(3)
print(next(it)) # 1
print(next(it)) # 2
5. 特点
-
内存高效、惰性计算:适合处理大型数据集(如文件逐行读取),因为迭代器不会一次性加载所有的数据,因此对内存的利用是高效的。
-
单向性:只能向前遍历,不能回退或重置。
-
无限序列:可以表示无限序列(如:
itertools.count()
) -
逐行读取大文件的示例:
with open("huge_file.txt") as file:for line in file: # file 对象是迭代器,不会一次性加载全部内容print(line)
6.常见的迭代器工具
-
itertools
模块
在Python中的itertools 模块提供了许多有用的迭代器import itertools# 无限迭代器 count_iter = itertools.count(1) # 1, 2, 3, ... cycle_iter = itertools.cycle("ABC") # A, B, C, A, B, C, ...# 有限迭代器 chain_iter = itertools.chain([1, 2], ["a", "b"]) # 1, 2, a, b
-
包含
yield
的生成器
生成器是一种特殊的迭代器
二、生成器(Generator)
1. 定义
- 生成器是一种特殊的迭代器,他的数据是通过
yield
关键字动态生成实现的,而不是预先存储在内存中,具有惰性计算的特性。 - 生成器函数在每次调用
next()
时执行到yield
处暂停,保留当前状态,下次继续执行。 - 生成器因为是迭代器的一种,因此也只能遍历依次,遍历结束后会跑出
StopIteration
2. 实现方式
-
生成器函数:使用
yield
关键字定义生成器函数。def count_up_to(n):num = 1while num <= n:yield num # 每次调用 next() 时返回 num,并暂停num += 1# 创建生成器对象 gen = count_up_to(3) print(next(gen)) # 1 print(next(gen)) # 2 print(next(gen)) # 3 # print(next(gen)) # 抛出StopIteration异常,因为生成器已经遍历完
-
生成器表达式:类似列表推导式,但使用圆括号
()
并返回生成器。
# 列表推导式(立即计算)
squares_list = [x ** 2 for x in range(3)] # [0, 1, 4]# 生成器表达式(惰性计算)
squares_gen = (x ** 2 for x in range(3))
print(next(squares_gen)) # 0
print(next(squares_gen)) # 1
3. yield
生成器的工作机制
- 了解
yield
的执行流程- 调用生成器函数时,返回一个生成器对象(不立即执行代码)。
- 首次调用
next() /send(None)
时,执行到第一个yield
,返回其值并 暂停。 - 再次调用
next()
时,从上次暂停的位置继续执行,直到下一个 yield。 - 如果函数结束或遇到
return
(表示函数结束),抛出StopIteration
异常错误。
- 生成器的状态保存
- 生成器会 记住局部变量和执行位置
- 生成器不会记住上次生成了什么,因此可以用
send()
传入值,直接告诉生成器上次生成了什么
- .
yield from
的介绍yield from
是 Python 3.3+ 引入的语法,用于简化生成器的嵌套调用,使生成器可以委托(delegate)给另一个生成器或可迭代对象。它的主要作用是:- 替代
for
循环 +yield
,让代码更简洁。# 传统方式使用yield (手动迭代子生成器) def generator1():for i in range(3):yield i def generator2():for j in generator1():yield j # 手动迭代 generator1for value in generator2():print(value) # 输出 0, 1, 2# 使用 yield from(自动委托) # 使用yield from 会自动遍历 range(3),并逐个 yield 值,无需手动循环 def generator1():yield from range(3) # 自动迭代 range(3)for value in generator1():print(value) # 输出 0, 1, 2
- 替代
# 使用 yield from 委托给另一个生成器def sub_generator():yield 1yield 2return "Done" # 返回值可以通过 yield from 获取def main_generator():# yield from 自动 `yield` `sub_generator` 的所有值。# 并且捕获 `sub_generator` 的 `return` 值(通过 `StopIteration.value` 获取)。result = yield from sub_generator() # 委托给 sub_generatorprint(f"子生成器返回值: {result}") # 输出 "Done"for value in main_generator():print(value) # 输出 1, 2# 输出:12子生成器返回值: Done```- **自动处理 `StopIteration`**,并获取子生成器的返回值。```pythondef sub_generator():print("子生成器启动")x = yield "请发送一个值"print(f"子生成器收到: {x}")yield f"处理后的值: {x * 2}"def main_generator():result = yield from sub_generator()print(f"子生成器返回值: {result}")gen = main_generator()print(next(gen)) # 输出 "请发送一个值"(启动子生成器)print(gen.send(10)) # 发送 10,输出 "子生成器收到: 10",返回 "处理后的值: 20"输出:子生成器启动请发送一个值子生成器收到: 10处理后的值: 20
3. yield from
与普通 yield
的对比
特性 | yield | yield from |
---|---|---|
代码简洁性 | 需要手动 for 循环迭代 | 自动委托,无需手动迭代 |
返回值处理 | 无法直接获取子生成器的返回值 | 可以获取子生成器的 return 值 |
异常传递 | 需要手动处理子生成器的异常 | 自动传递异常 |
5. 示例:yield
生成器可以串联,形成数据处理管道
# 生成偶数的生成器
def even_numbers(numbers):for num in numbers:if num % 2 == 0:yield num
# 将生成数字平方的生成器
def squared_numbers(numbers):for num in numbers:yield num ** 2# 组合生成器,生成偶数的平方
nums = range(10)
pipeline = squared_numbers(even_numbers(nums))
print(list(pipeline)) # [0, 4, 16, 36, 64]
6. 生成器表达式 vs 列表推导式:
# 生成器表达式(惰性计算)
gen = (x**2 for x in range(10)) # 列表推导式(立即计算)
lst = [x**2 for x in range(10)]
7. 特点
- 简洁性:自动实现
__iter__()
和__next__()
,无需手动编写类。 - 状态保存:每次
yield
会保留局部变量和代码执行位置。 - 一次性使用:遍历结束后无法重复使用。
三、迭代器 vs 生成器
特性 | 迭代器 | 生成器 |
---|---|---|
定义(实现方法) | 需手动实现 __iter__ 和 __next__ | 通过 yield 自动实现迭代器协议 |
内存利用 | 低(惰性计算) | 更低(更简洁、高效的实现) |
代码复杂度 | 较高(需编写类) | 低(函数 + yield ) |
灵活性 | 可自定义复杂逻辑 | 适合简单迭代逻辑 |
应用场景 | 需要复杂的迭代状态管理、精细控制迭代行为 ; 需要复用迭代器或添加额外方法。 | 需要快速实现惰性计算; 处理流式数据(如文件读取、网络请求); 简化迭代逻辑(如无限序列) |
迭代器和生成器都是处理大数据和流式数据的利器,根据场景灵活选择即可。