飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 Python简介

    • Python是什么
    • 安装与运行
    • 交互式解释器
    • 注释与编码规范
  • 第2章 变量与数据类型

    • 变量与对象
    • 整数 int
    • 浮点数 float
    • 复数 complex
    • 布尔值 bool
    • 字符串 str
    • 空值 None
    • 类型转换
  • 第3章 运算符与表达式

    • 算术运算符
    • 比较运算符
    • 赋值运算符
    • 逻辑运算符
    • 位运算符
    • 身份与成员运算符
    • 海象运算符
    • 运算符优先级
  • 第4章 流程控制

    • if 语句
    • if-else 语句
    • if-elif-else 语句
    • match-case 语句
    • 条件表达式(三元运算符)
    • while 循环
    • for 循环
    • range 函数
    • break 与 continue
    • 循环的 else 子句
    • pass 语句
  • 第5章 数据结构

    • 列表创建与索引
    • 列表方法
    • 列表推导式
    • 元组
    • 序列解包
    • 集合
    • 字典创建与访问
    • 字典方法
    • 字典推导式
    • range 对象
  • 第6章 函数

    • 定义函数
    • 位置参数与关键字参数
    • 默认参数
    • 可变参数
    • 解包实参
    • 函数返回值
    • lambda 表达式
    • 文档字符串与注解
    • 作用域与命名空间
    • global 与 nonlocal
  • 第7章 模块与包

    • 模块导入
    • 模块搜索路径
    • 包与相对导入
    • 标准库概览
  • 第8章 文件与输入输出

    • 文件读写
    • 上下文管理器
    • 字符串格式化
    • JSON 与 CSV
  • 第9章 面向对象

    • 类与对象
    • 方法
    • 实例变量与类变量
    • 私有变量
    • 继承
    • 多重继承
    • 魔术方法
    • 属性装饰器
    • 数据类 dataclass
  • 第10章 异常处理

    • 语法错误与异常
    • try-except
    • 异常链与 raise
    • 清理操作
    • 自定义异常
  • 第11章 迭代器与生成器

    • 迭代器协议
    • 生成器
    • 生成器表达式
    • 迭代工具
  • 第12章 高级特性

    • 装饰器
    • 函数式编程
  • 第13章 工程实践

    • 测试与调试
    • 代码质量
    • 虚拟环境

迭代器协议

迭代器是 Python 中实现惰性遍历的核心机制。理解迭代器协议,就能理解 for 循环背后的工作原理,也能让自己的类支持被 for 循环遍历。

可迭代对象与迭代器的区别

可迭代对象(Iterable)是指实现了 __iter__() 方法的对象,该方法返回一个迭代器。迭代器(Iterator)则是实现了 __iter__() 和 __next__() 的对象:__iter__() 返回自身,__next__() 每次返回序列中的下一个元素,当元素耗尽时抛出 StopIteration。

from collections.abc import Iterable, Iterator

# 列表是可迭代对象,但不是迭代器
staff = ["Alice", "Bob", "Charlie"]
print(isinstance(staff, Iterable))  # True
print(isinstance(staff, Iterator))   # False

# iter() 将可迭代对象转换为迭代器
it = iter(staff)
print(isinstance(it, Iterator))     # True
print(it is iter(it))               # True,迭代器的 __iter__ 返回自身

这个区别至关重要:可迭代对象可以反复创建新的迭代器来遍历,而迭代器本身是一次性的——一旦 __next__() 抛出 StopIteration,它就无法再次使用。

it = iter([1, 2, 3])
print(list(it))  # [1, 2, 3]
print(list(it))  # [],迭代器已耗尽

iter() 与 next()

iter() 是调用对象 __iter__() 方法的内置函数。next() 则调用迭代器的 __next__() 方法,并在元素耗尽时抛出 StopIteration。

s = "abc"
it = iter(s)

print(next(it))  # 'a'
print(next(it))  # 'b'
print(next(it))  # 'c'
print(next(it))  # StopIteration

next() 还支持第二个参数作为默认值。当迭代器耗尽时,返回默认值而不是抛出异常。这在处理长度不确定的序列时非常有用。

it = iter([1, 2])
print(next(it, "没了"))  # 1
print(next(it, "没了"))  # 2
print(next(it, "没了"))  # "没了"
print(next(it, "没了"))  # "没了",不会抛异常

for 循环的工作原理

for 语句本质上是对迭代器协议的语法糖。当执行 for x in iterable: 时,Python 在背后执行以下操作:

  1. 调用 iter(iterable) 获取迭代器。
  2. 反复调用 next(iterator),将返回值赋给循环变量 x。
  3. 当 next() 抛出 StopIteration 时,捕获该异常并结束循环。
# 以下两种写法等价
for char in "abc":
    print(char)

# 手动展开
_it = iter("abc")
while True:
    try:
        char = next(_it)
    except StopIteration:
        break
    print(char)

StopIteration 是迭代器协议的信号,不是真正的错误。它只应在迭代器的 __next__() 方法内部被抛出,绝不应该在生成器或普通函数中手动捕获或抛出——Python 3.7+ 中,生成器内部抛出的 StopIteration 会被转换为 RuntimeError,以防止混淆。

自定义迭代器类

为类添加迭代器行为只需实现两个方法:__iter__() 返回一个带有 __next__() 的对象。如果类本身实现了 __next__(),__iter__() 可以直接返回 self。

class Reverse:
    """反向遍历序列的迭代器。"""
    
    def __init__(self, data):
        self.data = data
        self.index = len(data)
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.index == 0:
            raise StopIteration
        self.index -= 1
        return self.data[self.index]

rev = Reverse("spam")
for char in rev:
    print(char)  # m, a, p, s

这个迭代器的状态保存在实例变量 self.index 中。每次调用 __next__() 时,索引递减并返回对应元素。当索引归零时抛出 StopIteration,通知消费者序列已耗尽。

独立迭代器 vs 可迭代对象

一个好的设计模式是:让可迭代对象和迭代器分离。可迭代对象每次调用 __iter__() 都返回一个全新的迭代器,支持多次遍历。

class Countdown:
    """可多次遍历的倒计时可迭代对象。"""
    
    def __init__(self, start):
        self.start = start
    
    def __iter__(self):
        # 每次返回新的迭代器,而不是 self
        return CountdownIterator(self.start)

class CountdownIterator:
    """真正的迭代器,负责维护遍历状态。"""
    
    def __init__(self, start):
        self.current = start
    
    def __iter__(self):
        return self
    
    def __next__(self):
        if self.current < 0:
            raise StopIteration
        num = self.current
        self.current -= 1
        return num

cd = Countdown(3)
print(list(cd))  # [3, 2, 1, 0]
print(list(cd))  # [3, 2, 1, 0],可以再次遍历

如果 Countdown.__iter__() 返回 self 并自己维护 current 状态,第二次遍历就会从 -1 开始,直接结束。分离设计让可迭代对象保持"纯净",迭代器承担"有状态"的职责。

迭代器的惰性特性

迭代器的核心价值在于惰性求值:它一次只产生一个元素,而不是一次性把所有元素加载到内存中。这使得处理大规模数据流成为可能。

def large_range(n):
    """返回 0 到 n-1 的迭代器,不占用 O(n) 内存。"""
    i = 0
    while i < n:
        yield i
        i += 1

# 处理 10 亿个数字,内存中只存一个整数
for num in large_range(1_000_000_000):
    if num > 5:
        break

文件对象本身就是迭代器,逐行读取文件时不会一次性加载全部内容:

with open("large.log") as f:
    for line in f:  # f 是迭代器,每次读一行
        if "ERROR" in line:
            print(line.strip())

迭代器协议的边界情况

  • 空迭代器:__next__() 第一次调用就抛出 StopIteration。
  • 无限迭代器:永远不抛出 StopIteration,如 itertools.count()。
  • 有副作用的迭代器:__next__() 可能修改内部状态或外部资源,如从网络流读取数据。
class LineReader:
    """逐行读取并自动跳过空行的迭代器。"""
    
    def __init__(self, lines):
        self.lines = iter(lines)
    
    def __iter__(self):
        return self
    
    def __next__(self):
        while True:
            line = next(self.lines)
            stripped = line.strip()
            if stripped:
                return stripped
        raise StopIteration

text = ["  ", "hello", "", "world", "  "]
reader = LineReader(text)
print(list(reader))  # ['hello', 'world']
下一页
生成器