Python迭代器与生成器详解
专为编程小白准备的通俗易懂指南,通过生活化比喻和代码示例掌握迭代器与生成器核心概念
迭代器 (Iterator)
生活比喻
迭代器就像自助餐厅的取餐流水线 – 你拿着餐盘依次经过各个菜品区,每次只能取一道菜,吃完才能取下一道。你不需要知道整个菜单,只需关注当前的菜品。
什么是迭代器?
迭代器是一个可以记住遍历位置的对象,它有两个基本方法:
- __iter__(): 返回迭代器对象本身
- __next__(): 返回容器中的下一个元素
为什么要使用迭代器?
- 不需要一次性加载所有数据到内存(节省内存)
- 提供统一的遍历接口(字符串、列表、字典等都可用)
- 支持惰性计算(需要时才生成数据)
优点
- 内存效率高(一次只处理一个元素)
- 通用遍历接口
- 支持无限序列
缺点
- 无法随机访问元素
- 只能向前遍历,不能后退
- 使用后元素消失(单向)
创建迭代器
# 自定义迭代器示例
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
else:
num = self.current
self.current -= 1
return num
# 使用迭代器
counter = CountDown(5)
for num in counter:
print(num) # 输出: 5, 4, 3, 2, 1
class CountDown:
def __init__(self, start):
self.current = start
def __iter__(self):
return self
def __next__(self):
if self.current <= 0:
raise StopIteration
else:
num = self.current
self.current -= 1
return num
# 使用迭代器
counter = CountDown(5)
for num in counter:
print(num) # 输出: 5, 4, 3, 2, 1
使用内置函数
# 将列表转换为迭代器
my_list = [1, 2, 3]
my_iter = iter(my_list)
print(next(my_iter)) # 输出: 1
print(next(my_iter)) # 输出: 2
print(next(my_iter)) # 输出: 3
# print(next(my_iter)) # 再次调用会引发StopIteration异常
my_list = [1, 2, 3]
my_iter = iter(my_list)
print(next(my_iter)) # 输出: 1
print(next(my_iter)) # 输出: 2
print(next(my_iter)) # 输出: 3
# print(next(my_iter)) # 再次调用会引发StopIteration异常
生成器 (Generator)
生活比喻
生成器就像自动售货机 – 你按一下按钮(调用next()),它就出来一件商品(生成一个值)。它不需要一次性准备所有商品,而是按需生产。
什么是生成器?
生成器是一种特殊类型的迭代器,创建更简单:
- 使用 yield 关键字
- 不需要定义 __iter__() 和 __next__() 方法
- 自动实现迭代器协议
生成器的两种创建方式
- 生成器函数:使用def定义,包含yield语句
- 生成器表达式:类似列表推导式,使用圆括号
优点
- 代码更简洁(自动实现迭代器协议)
- 状态自动保存(局部变量自动保存)
- 更高效的内存使用
- 支持管道式数据处理
缺点
- 只能迭代一次
- 没有长度属性
- 调试相对复杂
生成器函数示例
# 生成斐波那契数列
def fibonacci(max_num):
a, b = 0, 1
while a < max_num:
yield a # 暂停函数,返回a的值
a, b = b, a + b
# 使用生成器
for num in fibonacci(100):
print(num, end=’ ‘)
# 输出: 0 1 1 2 3 5 8 13 21 34 55 89
def fibonacci(max_num):
a, b = 0, 1
while a < max_num:
yield a # 暂停函数,返回a的值
a, b = b, a + b
# 使用生成器
for num in fibonacci(100):
print(num, end=’ ‘)
# 输出: 0 1 1 2 3 5 8 13 21 34 55 89
生成器表达式示例
# 列表推导式:一次性生成所有元素
squares_list = [x*x for x in range(10)]
# 生成器表达式:按需生成元素
squares_gen = (x*x for x in range(10))
print(squares_list) # 输出: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
print(squares_gen) # 输出: <generator object <genexpr> at 0x…>
# 使用生成器
for square in squares_gen:
print(square, end=’ ‘)
# 输出: 0 1 4 9 16 25 36 49 64 81
squares_list = [x*x for x in range(10)]
# 生成器表达式:按需生成元素
squares_gen = (x*x for x in range(10))
print(squares_list) # 输出: [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
print(squares_gen) # 输出: <generator object <genexpr> at 0x…>
# 使用生成器
for square in squares_gen:
print(square, end=’ ‘)
# 输出: 0 1 4 9 16 25 36 49 64 81
yield 关键字的高级用法
# 双向通信生成器
def interactive_gen():
name = yield “What’s your name?”
age = yield f”Hello {name}! How old are you?”
yield f”Wow, {age} is a great age!”
gen = interactive_gen()
print(next(gen)) # 输出: What’s your name?
print(gen.send(“Alice”)) # 输出: Hello Alice! How old are you?
print(gen.send(30)) # 输出: Wow, 30 is a great age!
def interactive_gen():
name = yield “What’s your name?”
age = yield f”Hello {name}! How old are you?”
yield f”Wow, {age} is a great age!”
gen = interactive_gen()
print(next(gen)) # 输出: What’s your name?
print(gen.send(“Alice”)) # 输出: Hello Alice! How old are you?
print(gen.send(30)) # 输出: Wow, 30 is a great age!
迭代器 vs 生成器
生活比喻
迭代器是工厂的传送带(需要专门设计),生成器是自动售货机(一键生成)。生成器是迭代器的”语法糖”(更简单的写法)。
核心区别
特性 | 迭代器 | 生成器 |
---|---|---|
定义方式 | 类(实现__iter__和__next__) | 函数(使用yield)或生成器表达式 |
内存使用 | 低(但取决于实现) | 极低(每次只生成一个值) |
代码复杂度 | 较高(需要实现两个方法) | 较低(自动实现协议) |
状态保存 | 手动保存 | 自动保存(局部冻结) |
使用场景 | 需要精确控制迭代过程 | 快速创建迭代器,处理大数据流 |
如何选择?
使用迭代器当:
- 需要设计复杂的状态管理
- 需要实现额外的迭代控制方法
- 需要在多个地方复用相同的迭代逻辑
使用生成器当:
- 快速实现简单迭代需求
- 处理大型数据流(文件、网络流)
- 需要节省内存的场景
- 实现管道式数据处理
实际应用场景
- 文件读取:逐行读取大文件而不耗尽内存
- 流式数据处理:处理网络数据流
- 无限序列:斐波那契数列、素数生成
- 管道处理:多个处理步骤串联
# 读取大文件的生成器应用
def read_large_file(file_path):
with open(file_path, ‘r’, encoding=’utf-8′) as file:
for line in file:
yield line.strip()
# 使用生成器处理10GB文件只需少量内存
for line in read_large_file(‘huge_log_file.txt’):
if ‘ERROR’ in line:
print(line)
def read_large_file(file_path):
with open(file_path, ‘r’, encoding=’utf-8′) as file:
for line in file:
yield line.strip()
# 使用生成器处理10GB文件只需少量内存
for line in read_large_file(‘huge_log_file.txt’):
if ‘ERROR’ in line:
print(line)