Python 迭代器与生成器
在Python编程中,处理数据集合和序列是一项常见任务。当我们需要遍历大量数据或者创建复杂的数据流时,迭代器和生成器就成为了非常强大的工具。它们不仅可以提高代码的性能,还能让我们的代码更加优雅和可读。
什么是迭代与迭代器
迭代的概念
迭代是指重复执行特定操作的过程,通常用于处理序列中的每个元素。在Python中,我们经常使用for
循环来迭代序列,如列表、元组、字典等。
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
print(fruit)
输出:
apple
banana
cherry
当你使用for
循环遍历一个对象时,Python实际上在背后调用了迭代器机制。
迭代器的定义
**迭代器(Iterator)**是一个对象,它实现了迭代器协议,包含两个方法:
__iter__()
: 返回迭代器对象本身__next__()
: 返回下一个值,如果没有更多的值可以返回,则引发StopIteration
异常
可迭代对象(Iterable)是任何可以返回迭代器的对象,它实现了__iter__()
方法。
使用内置函数操作迭代器
Python提供了一些内置函数来操作迭代器:
iter()
: 从可迭代对象创建迭代器next()
: 获取迭代器的下一个值
# 创建一个迭代器
my_list = [1, 2, 3]
my_iterator = iter(my_list)
# 使用next()获取元素
print(next(my_iterator)) # 1
print(next(my_iterator)) # 2
print(next(my_iterator)) # 3
# 当没有更多元素时,会引发StopIteration异常
try:
print(next(my_iterator))
except StopIteration:
print("迭代器已经用尽")
输出:
1
2
3
迭代器已经用尽
创建自定义迭代器
你可以创建自己的迭代器类,只需实现__iter__()
和__next__()
方法:
class CountDown:
def __init__(self, start):
self.start = start
def __iter__(self):
return self
def __next__(self):
if self.start <= 0:
raise StopIteration
self.start -= 1
return self.start + 1
# 使用自定义迭代器
countdown = CountDown(5)
for i in countdown:
print(i)
输出:
5
4
3
2
1
生成器:简化迭代器的创建
生成器(Generator)是创建迭代器的简单而强大的工具。它使用yield
语句而不是return
语句返回值,每次生成一个值后会"暂停"函数的执行,保存所有的状态,下次继续执行时从暂停的地方继续。
生成器函数
使用yield
关键字定义的函数就是生成器函数:
def simple_generator():
yield 1
yield 2
yield 3
# 使用生成器
gen = simple_generator()
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
输出:
1
2
3
生成器函数的一个常见实现是斐波那契数列:
def fibonacci(n):
a, b = 0, 1
count = 0
while count < n:
yield a
a, b = b, a + b
count += 1
# 生成前10个斐波那契数
for num in fibonacci(10):
print(num, end=' ')
输出:
0 1 1 2 3 5 8 13 21 34
生成器表达式
类似于列表推导式,Python也提供了生成器表达式,使用圆括号而不是方括号:
# 列表推导式
squares_list = [x**2 for x in range(5)] # 创建完整列表
print(squares_list)
# 生成器表达式
squares_gen = (x**2 for x in range(5)) # 创建生成器对象
for square in squares_gen:
print(square)
输出:
[0, 1, 4, 9, 16]
0
1
4
9
16
生成器表达式比列表推导式更节省内存,特别是处理大型数据集时。生成器表达式不会一次性创建整个序列,而是按需生成每个元素。
迭代器与生成器的优势
内存效率
迭代器和生成器的最大优势之一是它们的内存效率。它们不需要一次性将所有数据加载到内存中,而是按需生成数据:
# 假设我们要处理一个大文件的每一行
def read_large_file(file_path):
with open(file_path, 'r') as file:
for line in file: # 文件对象是一个迭代器
yield line.strip()
# 使用生成器处理文件,而不是一次性将整个文件读入内存
for line in read_large_file('example.txt'):
# 处理每一行...
pass
懒加载
迭代器实现了"懒加载"或"惰性求值",只在需要时才计算值:
def infinite_sequence():
num = 0
while True:
yield num
num += 1
# 虽然是"无限"序列,但我们只获取前5个
gen = infinite_sequence()
for _ in range(5):
print(next(gen))
输出:
0
1
2
3
4
链式操作
生成器可以进行链式操作,形成数据处理管道:
def numbers():
for i in range(1, 11):
yield i
def square(nums):
for num in nums:
yield num ** 2
def select_even(nums):
for num in nums:
if num % 2 == 0:
yield num
# 链式操作:生成1-10的数字,计算它们的平方,然后只选择偶数
pipeline = select_even(square(numbers()))
print(list(pipeline))
输出:
[4, 16, 36, 64, 100]
实际应用案例
分批处理数据
在处理大型数据集时,可以使用生成器分批处理数据:
def batch_processor(data, batch_size):
"""将数据分成固定大小的批次"""
for i in range(0, len(data), batch_size):
yield data[i:i + batch_size]
# 假设我们有一个大列表
large_data = list(range(1000))
# 每次处理100个元素
for batch in batch_processor(large_data, 100):
# 处理这一批数据
print(f"处理批次,大小: {len(batch)}")
输出:
处理批次,大小: 100
处理批次,大小: 100
处理批次,大小: 100
处理批次,大小: 100
处理批次,大小: 100
处理批次,大小: 100
处理批次,大小: 100
处理批次,大小: 100
处理批次,大小: 100
处理批次,大小: 100
数据转换管道
创建数据转换管道,进行复杂的数据处理:
def read_data(file_path):
"""读取CSV文件数据"""
with open(file_path, 'r') as f:
next(f) # 跳过标题行
for line in f:
yield line.strip().split(',')
def parse_data(lines):
"""解析每行数据"""
for line in lines:
# 假设第一列是名字,第二列是年龄
yield {'name': line[0], 'age': int(line[1])}
def filter_adults(people):
"""只保留成年人"""
for person in people:
if person['age'] >= 18:
yield person
# 使用管道处理数据
# pipeline = filter_adults(parse_data(read_data('people.csv')))
# for person in pipeline:
# print(f"{person['name']} is {person['age']} years old")
无限数据流
创建无限数据流,适用于需要持续生成数据的场景:
import time
import random
def sensor_data_simulator():
"""模拟传感器数据流"""
while True:
# 生成随机温度数据
yield {
'timestamp': time.time(),
'temperature': round(random.uniform(20, 30), 2)
}
time.sleep(1) # 每秒生成一次数据
# 模拟读取5秒的传感器数据
sensor = sensor_data_simulator()
for _ in range(5):
data = next(sensor)
print(f"时间: {data['timestamp']}, 温度: {data['temperature']}°C")
输出示例:
时间: 1634567890.123, 温度: 24.57°C
时间: 1634567891.124, 温度: 22.31°C
时间: 1634567892.125, 温度: 25.84°C
时间: 1634567893.126, 温度: 21.47°C
时间: 1634567894.127, 温度: 28.09°C
高级迭代器工具
Python的itertools
模块提供了许多用于创建和操作迭代器的函数:
import itertools
# 无限迭代器
count = itertools.count(10) # 从10开始计数
print([next(count) for _ in range(5)]) # [10, 11, 12, 13, 14]
# 循环迭代器
cycle = itertools.cycle(['A', 'B', 'C'])
print([next(cycle) for _ in range(7)]) # ['A', 'B', 'C', 'A', 'B', 'C', 'A']
# 组合迭代器
print(list(itertools.combinations([1, 2, 3, 4], 2))) # 所有可能的二元组合
输出:
[10, 11, 12, 13, 14]
['A', 'B', 'C', 'A', 'B', 'C', 'A']
[(1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)]
生成器的高级特性
send 方法
生成器还有一个send()
方法,它可以向生成器内部"发送"值:
def echo_generator():
value = yield "Ready"
print(f"Received: {value}")
while True:
value = yield value
print(f"Received: {value}")
gen = echo_generator()
print(next(gen)) # 启动生成器,打印"Ready"
print(gen.send("Hello")) # 发送"Hello"到生成器
print(gen.send(42)) # 发送42到生成器
输出:
Ready
Received: Hello
Hello
Received: 42
42
throw 和 close 方法
生成器还有throw()
和close()
方法,用于抛出异常和关闭生成器:
def sample_generator():
try:
yield "First"
yield "Second"
yield "Third"
except Exception as e:
print(f"Exception caught: {e}")
yield "Exception handled"
finally:
print("Generator is closing")
gen = sample_generator()
print(next(gen)) # First
print(gen.throw(ValueError("Custom error"))) # 抛出一个异常到生成器内部
print(next(gen)) # Third
gen.close() # 关闭生成器
输出:
First
Exception caught: Custom error
Exception handled
Third
Generator is closing
总结
迭代器和生成器是Python中处理序列数据的强大工具。它们提供了内存效率高、处理能力强的解决方案,特别适合处理大型数据集或无限数据流。
主要优点包括:
- 惰性求值,节省内存
- 可以处理无限大的数据流
- 代码简洁易读
- 可以组合成数据处理管道
- 迭代器是实现
__iter__()
和__next__()
方法的对象 - 生成器是使用
yield
语句创建的特殊迭代器 - 生成器表达式是创建生成器的简洁方法
itertools
模块提供了丰富的迭代器工具
练习
为了巩固所学知识,尝试完成以下练习:
- 创建一个生成器,生成斐波那契数列的前N个数
- 编写一个自定义迭代器类,实现倒计时功能
- 使用生成器表达式找出1到1000中所有能被3和5同时整除的数
- 实现一个文件读取生成器,每次返回一个随机行
- 使用
itertools
模块中的函数生成所有可能的扑克牌组合
进一步学习资源
- Python官方文档: 迭代器和生成器
- Python的itertools模块
- 《Fluent Python》书籍中关于迭代器和生成器的章节
- Python Cookbook - 迭代器和生成器相关内容
掌握迭代器和生成器可以帮助你编写更加高效、优雅的Python代码。随着你的Python编程能力的提升,这些工具将成为你解决复杂问题的重要武器!