Skip to content

01-迭代基础

Python 版本要求:Python 3.11+

贯穿项目:日志分析系统
本节目标:理解 for 循环如何逐行读取大日志文件


概念铺垫

为什么需要理解迭代?

问题场景:大日志文件处理

你需要处理一个 10GB 的日志文件,找出所有错误:

python
# ❌ 一次性读取:内存爆炸
with open("huge.log", "r") as f:
    lines = f.readlines()  # 10GB 装入内存!程序崩溃

# ✅ 逐行处理:内存友好
with open("huge.log", "r") as f:
    for line in f:  # 每次只加载一行
        if "ERROR" in line:
            print(line)

问题:为什么 for line in f 可以处理 10GB 文件?背后发生了什么?


生活类比

把迭代想象成音乐播放:

┌─────────────────────────────────────────┐
│  音乐播放类比                             │
├─────────────────────────────────────────┤
│                                         │
│  可迭代对象 = 播放列表                    │
│  • 存了很多歌                            │
│  • 你可以"遍历"它                        │
│  • 可以多次播放                          │
│                                         │
│  迭代器 = 播放指针                        │
│  • 知道当前播放到哪一首                   │
│  • 每次点击"下一首"就前进                 │
│  • 用完就失效(一次性)                   │
│                                         │
│  for循环 = 自动播放                       │
│  • 自动点击"下一首"                       │
│  • 播完列表自动停止                       │
│                                         │
│  关键理解:                               │
│  • 播放列表 ≠ 播放指针                    │
│  • list ≠ iterator                       │
│                                         │
└─────────────────────────────────────────┘

一句话:

可迭代对象是"播放列表",迭代器是"播放指针"


核心概念

可迭代对象 vs 迭代器

python
from collections.abc import Iterable, Iterator

# 判断类型
my_list = [1, 2, 3]
my_iter = iter(my_list)

print(f"list 可迭代: {isinstance(my_list, Iterable)}")   # True
print(f"list 是迭代器: {isinstance(my_list, Iterator)}")  # False ← 关键!

print(f"iter 可迭代: {isinstance(my_iter, Iterable)}")   # True
print(f"iter 是迭代器: {isinstance(my_iter, Iterator)}") # True

关键结论:

对象是否可迭代是否迭代器类比
list播放列表
str播放列表
dict播放列表
iter(list)播放指针
┌─────────────────────────────────────────────────────────────┐
│          迭代概念关系                                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   可迭代对象                         │
│   ───────────────────                                       │
│   • 可以使用 for...in 遍历的对象                             │
│   • 实现了 __iter__() 方法                                  │
│   • 例如:list, tuple, str, dict, set, range               │
│                                                             │
│         ▼ iter() 获取                                       │
│                                                             │
│   迭代器                             │
│   ───────────────                                           │
│   • 可以被 next() 调用的对象                                │
│   • 实现了 __iter__() 和 __next__() 方法                    │
│   • 每次返回一个值,直到耗尽                               │
│   • 只能用一次,用完即弃                                   │
│                                                             │
│   ⚠️ 所有迭代器都是可迭代对象                               │
│   ⚠️ 但可迭代对象不一定是迭代器                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘

L1 理解层:会用

for 循环的工作原理

执行流程时间线

┌─────────────────────────────────────────────────────────────┐
│  for 循环执行流程时间线                                       │
│                                                             │
│  代码:for line in file:                                    │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐   │
│  │                                                      │   │
│  │  ① iter(file) ───→ 创建迭代器                       │   │
│  │        │                                             │   │
│  │        ↓                                             │   │
│  │  ② next(iterator) ─→ 返回第1行                      │   │
│  │        │                                             │   │
│  │        ↓                                             │   │
│  │  ③ 执行循环体 ───→ 处理第1行                        │   │
│  │        │                                             │   │
│  │        ↓                                             │   │
│  │  ④ next(iterator) ─→ 返回第2行                      │   │
│  │        │                                             │   │
│  │        ↓                                             │   │
│  │  ⑤ 执行循环体 ───→ 处理第2行                        │   │
│  │        │                                             │   │
│  │        ...                                           │   │
│  │        ↓                                             │   │
│  │  ⑥ next(iterator) ─→ StopIteration                 │   │
│  │        │                                             │   │
│  │        ↓                                             │   │
│  │  ⑦ 捕获异常 ─────→ 循环结束                         │   │
│  │                                                      │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

for 循环等价展开

你平时写的:

python
numbers = [1, 2, 3]
for num in numbers:
    print(num)

等价于:

python
numbers = [1, 2, 3]
iterator = iter(numbers)  # ① 获取迭代器

while True:
    try:
        num = next(iterator)  # ② 获取下一个
        print(num)            # ③ 执行循环体
    except StopIteration:     # ④ 迭代结束
        break

手动控制迭代

python
numbers = [1, 2, 3]
iterator = iter(numbers)

print(next(iterator))  # 1
print(next(iterator))  # 2
print(next(iterator))  # 3

# 耗尽后用默认值避免异常
print(next(iterator, "已耗尽"))  # "已耗尽"

贯穿实战:日志文件处理

python
from typing import Iterator

def filter_errors(filename: str) -> Iterator[str]:
    """过滤日志中的错误行
    
    贯穿项目:日志分析系统第1步
    功能:从大日志文件中提取 ERROR 行
    内存:无论文件多大,只占用当前行的内存
    """
    with open(filename, "r", encoding="utf-8") as f:
        for line in f:           # 文件对象是迭代器
            if "ERROR" in line:
                yield line.strip()  # 惰性产出

# 使用:处理10GB日志文件
for error in filter_errors("app.log"):
    print(error[:100])  # 只打印前100字符

关键点:

代码含义
for line in f文件对象是迭代器,逐行读取
yield生成器,惰性产出结果
内存占用只占用当前行,与文件大小无关

迭代器协议补充

最简示例

python
from typing import Iterator

numbers = [1, 2, 3]
iterator: Iterator[int] = iter(numbers)

print(next(iterator))  # 1
print(next(iterator))  # 2
print(next(iterator))  # 3
print(next(iterator, "耗尽"))  # "耗尽"

迭代器协议详解

┌─────────────────────────────────────────────────────────────┐
│  迭代器协议                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  可迭代对象必须实现:                                        │
│  ─────────────────────                                      │
│  __iter__() → 返回迭代器对象                                │
│                                                             │
│  迭代器必须实现:                                            │
│  ─────────────────────                                      │
│  __iter__() → 返回 self                                     │
│  __next__() → 返回下一个值或抛 StopIteration                │
│                                                             │
│  示例:                                                      │
│  my_list = [1, 2, 3]                                        │
│  hasattr(my_list, '__iter__')  # True                      │
│  hasattr(my_list, '__next__')  # False ← 不是迭代器         │
│                                                             │
│  my_iter = iter(my_list)                                    │
│  hasattr(my_iter, '__iter__')  # True                      │
│  hasattr(my_iter, '__next__')  # True ← 是迭代器            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

L2 实践层:用好

推荐做法

做法原因示例
优先用 for 循环自动处理 StopIterationfor item in items: process(item)
大文件逐行处理内存友好for line in open('file.txt'): ...
迭代器耗尽后重新获取迭代器是一次性的it = iter(items) 每次重新调用
用 isinstance 检查类型明确区分isinstance(obj, Iterator)

反模式:不要这样做

python
# ❌ 迭代器耗尽后继续使用
numbers = [1, 2, 3]
it = iter(numbers)
print(list(it))  # [1, 2, 3]
print(list(it))  # [] ← 已耗尽!

# ✅ 正确:重新获取
numbers = [1, 2, 3]
print(list(iter(numbers)))  # [1, 2, 3]
print(list(iter(numbers)))  # [1, 2, 3]
python
# ❌ 迭代时修改被迭代对象
items = [1, 2, 3, 4]
for item in items:
    if item == 2:
        items.remove(item)  # 危险!可能遗漏元素

# ✅ 正确:遍历副本
items = [1, 2, 3, 4]
for item in items.copy():
    if item == 2:
        items.remove(item)

适用场景

场景是否推荐原因
遍历列表/字典✅ 推荐for 循环简洁高效
处理大文件(GB级)✅ 推荐逐行读取,内存友好
处理无限数据流✅ 推荐惰性计算,按需获取
多次遍历同一数据❌ 不推荐迭代器迭代器是一次性的
需要随机访问❌ 不推荐迭代器用列表更合适

L3 专家层:深入

迭代器协议实现

python
from collections.abc import Iterable, Iterator

# 检查对象的方法
my_list = [1, 2, 3]
print(hasattr(my_list, '__iter__'))  # True(可迭代)
print(hasattr(my_list, '__next__'))  # False(不是迭代器)

my_iter = iter(my_list)
print(hasattr(my_iter, '__iter__'))  # True
print(hasattr(my_iter, '__next__'))  # True(是迭代器)

# 手动调用底层方法
it = iter([1, 2, 3])
print(it.__next__())  # 1(等价于 next(it))
print(it.__next__())  # 2
print(it.__next__())  # 3

for 循环字节码

python
import dis

def for_loop_example():
    for x in [1, 2, 3]:
        print(x)

dis.dis(for_loop_example)
# 输出包含 GET_ITER, FOR_ITER 等专门指令
┌─────────────────────────────────────────────────────────────┐
│  for 循环字节码                                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  GET_ITER     → 获取迭代器                                   │
│  FOR_ITER     → 调用 next() 并检查 StopIteration            │
│  STORE_FAST   → 存储当前元素                                 │
│  ... 循环体 ...                                              │
│  JUMP_BACK    → 返回 FOR_ITER                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘

性能考量

操作时间复杂度空间复杂度说明
iter(iterable)O(1)O(1)只创建迭代器对象
next(iterator)O(1)O(1)获取一个元素
for item in iterableO(n)O(1)每次只处理一个元素
list(iterator)O(n)O(n)将所有元素转为列表
python
import sys

# 内存对比
list_data = [x for x in range(1_000_000)]
gen_data = (x for x in range(1_000_000))

print(f"列表内存:{sys.getsizeof(list_data) / 1024:.2f} KB")  # 约 8 MB
print(f"迭代器内存:{sys.getsizeof(gen_data)} 字节")          # 约 200 字节

为什么迭代器是一次性的?

┌─────────────────────────────────────────────────────────────┐
│  迭代器一次性设计原因                                        │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ① 内存效率                                                 │
│  迭代器不存储所有数据,只维护当前状态指针                    │
│                                                             │
│  ② 简化实现                                                 │
│  无需复杂的回溯和重置逻辑                                   │
│                                                             │
│  ③ 支持无限序列                                             │
│  无限迭代器无法"重置"(没有终点)                           │
│                                                             │
│  ④ 状态一致性                                               │
│  避免多次遍历导致状态混乱                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

自检清单

回答以下问题,检查你是否掌握了核心概念:

  1. list 是迭代器还是可迭代对象?
  2. iter([1,2,3]) 返回什么?
  3. 为什么迭代器只能用一次?
  4. for line in open('file') 为什么可以处理大文件?

答案:

  1. list 是可迭代对象,不是迭代器
  2. 返回一个 list_iterator 对象
  3. 迭代器只维护当前位置指针,用完即弃
  4. 文件对象是迭代器,每次只加载一行到内存

本章能力清单

学完本章,你能够:

  • [x] 区分可迭代对象和迭代器
  • [x] 理解 for 循环背后的 iter/next 机制
  • [x] 用 for 循环逐行处理大文件
  • [x] 手动使用 iter() 和 next() 控制迭代

下一步学习:

  • 自定义迭代器 → 第2节
  • 生成器基础 → 第3节

本章小结

┌─────────────────────────────────────────────────────────────┐
│                    迭代基础 知识要点                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   核心概念:                                                 │
│   ✓ 可迭代对象:播放列表(可以遍历)                         │
│   ✓ 迭代器:播放指针(用来遍历,一次性)                     │
│                                                             │
│   for 循环原理:                                             │
│   ✓ iter() 获取迭代器                                       │
│   ✓ next() 获取下一个元素                                   │
│   ✓ StopIteration 结束循环                                  │
│                                                             │
│   关键区别:                                                 │
│   ✓ list 是可迭代对象,iter(list) 是迭代器                  │
│   ✓ 迭代器只能用一次,可迭代对象可以多次遍历                 │
│                                                             │
│   应用场景:                                                 │
│   ✓ 大文件逐行处理                                          │
│   ✓ 数据流处理                                              │
│   ✓ 惰性计算                                                │
│                                                             │
└─────────────────────────────────────────────────────────────┘