迭代器协议
迭代器(Iterator)是 Python 中遍历集合的统一接口。理解迭代器协议,就能理解 for 循环、list()、sum() 等内置功能的工作原理,也能写出支持遍历的自定义对象。
迭代器协议
迭代器协议要求对象实现两个方法:
__iter__():返回迭代器对象自身next():返回下一个元素,没有元素时抛出StopIteration
class CountDown(object):
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
# 使用
for num in CountDown(5):
print num,
# 输出:5 4 3 2 1
for 循环的执行过程:
- 调用
iter(obj),即obj.__iter__(),获取迭代器 - 反复调用迭代器的
next() - 捕获
StopIteration,循环结束
可迭代对象 vs 迭代器
可迭代对象(Iterable)只需实现 __iter__(),返回一个迭代器:
class Range(object):
def __init__(self, n):
self.n = n
def __iter__(self):
return RangeIterator(self.n)
class RangeIterator(object):
def __init__(self, n):
self.i = 0
self.n = n
def __iter__(self):
return self
def next(self):
if self.i >= self.n:
raise StopIteration
i = self.i
self.i += 1
return i
for i in Range(5):
print i,
# 输出:0 1 2 3 4
Range 是可迭代对象,RangeIterator 是迭代器。一个对象可以同时是可迭代对象和迭代器(__iter__() 返回 self),但更好的设计是分离两者——可迭代对象可以多次遍历,迭代器是一次性的。
迭代器是一次性的
迭代器遍历完就耗尽,不能重置:
it = iter([1, 2, 3])
print list(it) # [1, 2, 3]
print list(it) # [] —— 已经耗尽
如果需要多次遍历,应该创建新的迭代器:
class ReusableRange(object):
def __init__(self, n):
self.n = n
def __iter__(self):
return RangeIterator(self.n) # 每次创建新的迭代器
内置迭代工具
iter() 函数获取迭代器:
it = iter([1, 2, 3])
print it.next() # 1
print it.next() # 2
print it.next() # 3
print it.next() # StopIteration
next() 函数(Python 2.6+)是 it.next() 的函数形式:
it = iter([1, 2, 3])
print next(it) # 1
print next(it, "default") # 2, 3, "default"(不抛出 StopIteration)
迭代器与列表的区别
| 特性 | 列表 | 迭代器 |
|---|---|---|
| 内存占用 | 存储所有元素 | 惰性计算,一次一个 |
| 可重复遍历 | 是 | 否(一次性) |
| 长度 | len() 可用 | 不知道长度 |
| 索引访问 | list[i] | 不支持 |
# 列表:占用内存大
big_list = range(1000000) # 创建百万个整数的列表
# 迭代器:占用内存小
big_iter = xrange(1000000) # 惰性生成,不存储所有值
实际应用
自定义文件读取器:
class LineReader(object):
def __init__(self, filename):
self.filename = filename
def __iter__(self):
return self
def next(self):
line = self.file.readline()
if not line:
self.file.close()
raise StopIteration
return line.strip()
def __enter__(self):
self.file = open(self.filename, "r")
return self
def __exit__(self, *args):
self.file.close()
with LineReader("data.txt") as reader:
for line in reader:
print line
无限序列:
class Fibonacci(object):
def __init__(self):
self.a, self.b = 0, 1
def __iter__(self):
return self
def next(self):
result = self.a
self.a, self.b = self.b, self.a + self.b
return result
fib = Fibonacci()
for i, num in enumerate(fib):
if i >= 10:
break
print num,
# 输出:0 1 1 2 3 5 8 13 21 34
迭代器与生成器
手动实现迭代器比较繁琐。Python 提供了生成器(Generator),用函数语法自动实现迭代器协议。生成器是迭代器的语法糖,将在下一节详细讲解。