1. 为什么我们需要迭代器?
第一次接触Python时,我们都写过这样的代码:
python复制for item in [1, 2, 3]:
print(item)
看起来简单直观,但你想过为什么几乎所有Python容器(列表、元组、字典等)都能用for循环遍历吗?这背后就是迭代器协议在发挥作用。
迭代器(Iterator)是Python中最容易被忽视却又无处不在的设计模式。它本质上是一种数据流抽象,允许我们按需访问集合元素而不必暴露底层实现。想象你在读一本电子书——你不需要一次性下载整本书,而是可以逐页加载,迭代器就是这种"按需获取"机制的编程实现。
关键理解:迭代器不是数据的容器,而是访问容器中元素的协议。它定义了"如何获取下一个元素"的标准方式。
2. 迭代器协议详解
2.1 魔法方法__iter__和__next__
Python通过两个特殊方法实现迭代器协议:
__iter__():返回迭代器对象本身__next__():返回下一个元素,耗尽时抛出StopIteration
让我们用经典的斐波那契数列实现一个迭代器:
python复制class Fibonacci:
def __init__(self, limit):
self.prev = 0
self.curr = 1
self.limit = limit
def __iter__(self):
return self
def __next__(self):
if self.curr > self.limit:
raise StopIteration
result = self.curr
self.curr, self.prev = self.curr + self.prev, self.curr
return result
# 使用示例
for num in Fibonacci(100):
print(num) # 输出1,1,2,3,5,8,13,21,34,55,89
2.2 迭代器 vs 可迭代对象
新手常混淆这两个概念:
- 可迭代对象(Iterable):实现了
__iter__()方法的对象(如list, dict) - 迭代器(Iterator):同时实现
__iter__()和__next__()的对象
验证方法:
python复制from collections.abc import Iterator, Iterable
nums = [1,2,3]
print(isinstance(nums, Iterable)) # True
print(isinstance(nums, Iterator)) # False
print(isinstance(iter(nums), Iterator)) # True
3. for循环的幕后真相
当执行for x in obj时,Python解释器会:
- 调用
iter(obj)获取迭代器 - 重复调用
next()直到捕获StopIteration - 自动处理异常终止循环
等效代码:
python复制iterator = iter(obj)
while True:
try:
x = next(iterator)
# 循环体代码
except StopIteration:
break
4. 实际应用中的迭代器技巧
4.1 惰性求值与内存优化
迭代器的核心优势是惰性计算——只在需要时生成数据。对比两种实现方式:
python复制# 传统列表(立即计算)
def get_squares(n):
return [x**2 for x in range(n)] # 立即生成所有元素
# 迭代器版本(惰性计算)
def get_squares_iter(n):
for x in range(n):
yield x**2 # 每次调用生成一个
# 内存占用对比
import sys
print(sys.getsizeof(get_squares(1000000))) # 约8.4MB
print(sys.getsizeof(get_squares_iter(1000000))) # 仅112字节
4.2 无限序列处理
迭代器可以表示无限序列,这是列表无法做到的:
python复制import itertools
# 无限计数器
counter = itertools.count(start=10, step=2)
print(next(counter)) # 10
print(next(counter)) # 12
# 可以无限继续...
# 循环迭代
cycle = itertools.cycle(['A','B','C'])
print([next(cycle) for _ in range(5)]) # ['A','B','C','A','B']
4.3 链式操作
itertools模块提供了强大的迭代器操作工具:
python复制import itertools
# 合并多个迭代器
chain = itertools.chain([1,2], 'AB', (x for x in range(3)))
print(list(chain)) # [1,2,'A','B',0,1,2]
# 分组操作
groups = itertools.groupby('AAABBBCCAAA')
for key, group in groups:
print(f"{key}: {list(group)}")
# 输出:
# A: ['A','A','A']
# B: ['B','B','B']
# C: ['C','C']
# A: ['A','A','A']
5. 常见误区与调试技巧
5.1 一次性消费问题
迭代器是"一次性"的,耗尽后无法重用:
python复制numbers = iter([1,2,3])
print(list(numbers)) # [1,2,3]
print(list(numbers)) # [] 第二次为空!
解决方案:
- 重新创建迭代器
- 使用
itertools.tee复制迭代器 - 转换为列表(失去惰性优势)
5.2 迭代过程中修改容器
在迭代可变序列时修改它会导致异常:
python复制numbers = [1,2,3,4]
for x in numbers:
if x == 2:
numbers.remove(1) # RuntimeError
安全做法:
- 创建副本:
for x in list(numbers) - 使用while循环手动控制索引
5.3 性能陷阱
虽然迭代器节省内存,但并非总是最快选择。测试不同场景:
python复制from timeit import timeit
# 小数据量测试
print(timeit("[x**2 for x in range(100)]", number=10000)) # 约0.1秒
print(timeit("list(x**2 for x in range(100))", number=10000)) # 约0.15秒
# 大数据量测试
print(timeit("[x**2 for x in range(1000000)]", number=10)) # 约2秒,内存占用高
print(timeit("list(x**2 for x in range(1000000))", number=10)) # 约2.5秒,内存占用低
6. 高级应用:自定义迭代器模式
6.1 反向迭代器
实现一个支持反向遍历的列表:
python复制class ReversibleList:
def __init__(self, data):
self.data = data
def __iter__(self):
return iter(self.data)
def __reversed__(self):
return reversed(self.data)
rl = ReversibleList([1,2,3])
for x in reversed(rl):
print(x) # 3,2,1
6.2 分页迭代器
模拟数据库分页查询:
python复制class Paginator:
def __init__(self, total, per_page=10):
self.total = total
self.per_page = per_page
def __iter__(self):
self.current = 0
return self
def __next__(self):
if self.current >= self.total:
raise StopIteration
start = self.current
end = min(self.current + self.per_page, self.total)
self.current = end
return (start, end)
# 使用示例
for page in Paginator(53, 10):
print(f"Query rows {page[0]} to {page[1]}")
# 输出:
# Query rows 0 to 10
# Query rows 10 to 20
# ...
# Query rows 50 to 53
6.3 过滤迭代器
实现带过滤条件的迭代:
python复制class FilterIterator:
def __init__(self, iterable, predicate):
self.iterator = iter(iterable)
self.predicate = predicate
def __iter__(self):
return self
def __next__(self):
while True:
item = next(self.iterator)
if self.predicate(item):
return item
# 使用示例
evens = FilterIterator(range(10), lambda x: x%2==0)
print(list(evens)) # [0,2,4,6,8]
7. 迭代器与生成器的关系
生成器(Generator)是迭代器的语法糖,使用yield关键字简化实现。对比两种写法:
python复制# 迭代器类写法
class Squares:
def __init__(self, limit):
self.limit = limit
self.n = 0
def __iter__(self):
return self
def __next__(self):
if self.n >= self.limit:
raise StopIteration
result = self.n ** 2
self.n += 1
return result
# 生成器函数写法
def squares_gen(limit):
n = 0
while n < limit:
yield n ** 2
n += 1
# 使用完全一致
for x in Squares(5):
print(x)
for x in squares_gen(5):
print(x)
生成器表达式则更简洁:
python复制squares = (x**2 for x in range(5)) # 生成器表达式
8. 现代Python中的迭代器工具
8.1 enumerate和zip的迭代器本质
常用内置函数实际返回迭代器:
python复制letters = ['A','B','C']
print(type(enumerate(letters))) # <class 'enumerate'>
print(type(zip([1,2], letters))) # <class 'zip'>
# 典型用法
for i, letter in enumerate(letters, start=1):
print(f"{i}. {letter}")
8.2 异步迭代器(Python 3.6+)
异步编程中也有迭代器模式:
python复制import asyncio
class AsyncCounter:
def __init__(self, limit):
self.limit = limit
self.current = 0
def __aiter__(self):
return self
async def __anext__(self):
if self.current >= self.limit:
raise StopAsyncIteration
await asyncio.sleep(0.1) # 模拟IO操作
self.current += 1
return self.current
async def main():
async for num in AsyncCounter(3):
print(num) # 1,2,3
asyncio.run(main())
8.3 类型注解中的迭代器
Python 3.9+的类型系统支持精确标注:
python复制from typing import Iterator, Iterable
def count_up_to(n: int) -> Iterator[int]:
i = 0
while i < n:
yield i
i += 1
def process(items: Iterable[str]) -> list[str]:
return [item.upper() for item in items]
9. 性能优化实战
9.1 避免不必要的迭代
常见低效模式:
python复制# 反例:多次迭代同一数据
data = [x for x in range(1000000)]
sum_data = sum(data) # 第一次完整迭代
max_data = max(data) # 第二次完整迭代
# 正例:单次迭代完成多项计算
sum_data, max_data = 0, 0
for x in data:
sum_data += x
if x > max_data:
max_data = x
9.2 选择正确的迭代方式
不同场景的性能对比:
python复制from timeit import timeit
setup = "data = [x for x in range(10000)]"
# 1. 直接索引访问
print(timeit("for i in range(len(data)): data[i]", setup, number=1000))
# 2. 直接迭代元素
print(timeit("for x in data: x", setup, number=1000))
# 3. 使用enumerate
print(timeit("for i,x in enumerate(data): x", setup, number=1000))
# 4. 使用zip迭代多个列表
setup += "; data2 = data.copy()"
print(timeit("for x,y in zip(data,data2): x+y", setup, number=1000))
9.3 内存视图与高效迭代
处理大型数值数据时,memoryview可以避免复制:
python复制import array
# 创建大型数组
arr = array.array('d', [x/100 for x in range(10**6)])
# 普通切片会复制数据
subset = arr[100000:200000] # 复制800KB数据
# 使用memoryview零拷贝
mv = memoryview(arr)
subset_view = mv[100000:200000] # 不复制数据
# 迭代处理
sum_sq = 0
for x in subset_view: # 直接访问原始数据
sum_sq += x*x
10. 设计模式与迭代器
10.1 迭代器模式在GUI中的应用
模拟事件循环中的迭代:
python复制class EventQueue:
def __init__(self):
self.events = []
def push(self, event):
self.events.append(event)
def __iter__(self):
while self.events:
yield self.events.pop(0)
# 使用示例
queue = EventQueue()
queue.push("click")
queue.push("keypress")
queue.push("close")
for event in queue:
print(f"Processing: {event}")
if event == "close":
break
10.2 组合迭代器
树形结构的深度优先遍历:
python复制class TreeNode:
def __init__(self, value, children=None):
self.value = value
self.children = children or []
def __iter__(self):
yield self.value
for child in self.children:
yield from child
# 构建树
tree = TreeNode(1, [
TreeNode(2, [
TreeNode(4),
TreeNode(5)
]),
TreeNode(3)
])
# 遍历
print(list(tree)) # [1,2,4,5,3]
10.3 状态迭代器
带状态的迭代器示例:
python复制class StatefulIterator:
def __init__(self, data):
self.data = data
self.state = "start"
def __iter__(self):
self.index = 0
return self
def __next__(self):
if self.index >= len(self.data):
self.state = "end"
raise StopIteration
item = self.data[self.index]
self.index += 1
if item < 0:
self.state = "negative"
elif item > 10:
self.state = "large"
else:
self.state = "normal"
return item
# 使用示例
iterator = StatefulIterator([1, -2, 15, 3])
for x in iterator:
print(f"{x} (state: {iterator.state})")
迭代器是Python中看似简单实则精妙的设计,理解它的工作原理不仅能写出更高效的代码,还能深入理解Python的设计哲学。在实际项目中,合理运用迭代器模式可以显著提升代码的可读性和性能,特别是在处理大数据流或复杂数据结构时。
