当前位置:首页>python>Python迭代器与生成器 (学习笔记6)

Python迭代器与生成器 (学习笔记6)

  • 2026-10-11 06:26:03
Python迭代器与生成器 (学习笔记6)

Python 迭代器与生成器

一句话理解: 迭代器像是一个"取号机"——你每次叫号,它就给你下一个号,直到号用完为止;生成器是一种"懒惰的工厂"——你要一个它才生产一个,不会提前把所有产品都造出来。



    什么是迭代?

    迭代就是"一个接一个地取值",比如:

    for x in [1, 2, 3]:   # 每次循环取出一个元素    print(x)

    能被 for 循环遍历的对象叫做可迭代对象(Iterable),常见的有:列表、元组、字符串、字典、集合等。

    可迭代对象 vs 迭代器

    • 可迭代对象:可以用 iter() 函数"激活"它,变成迭代器
    • 迭代器:在可迭代对象基础上,额外支持 next() 一个一个取值
    可迭代对象  ──iter()──>  迭代器  ──next()──>  逐个取值[1,2,3]                  <it>               1 → 2 → 3 → StopIteration

    迭代器

    iter() 与 next()

    iter() 把一个可迭代对象转换成迭代器;next() 从迭代器中取下一个值。

    nums = [1, 2, 3, 4]it = iter(nums)       # 创建迭代器,相当于"拿到取号机"print(next(it))       # 输出: 1  (取第1个号)print(next(it))       # 输出: 2  (取第2个号)print(next(it))       # 输出: 3print(next(it))       # 输出: 4# print(next(it))     # 如果继续取,会抛出 StopIteration,表示没有更多了

    关键点: 迭代器只能往前走,不能回头。就像排队叫号,叫过的号不会再叫。


    for 循环遍历

    for 循环本质上就是在自动调用 iter() + 反复调用 next(),直到捕获 StopIteration 为止:

    nums = [1, 2, 3, 4]it = iter(nums)for x in it:    print(x, end=" ")# 输出: 1 2 3 4

    上面的 for 循环等价于下面的手动版本:

    # for 循环的内部原理(伪代码)it = iter(nums)whileTrue:try:        x = next(it)        print(x, end=" ")except StopIteration:break# 没有更多元素了,停止循环

    用 while + next() 手动遍历

    有时候 for 循环不够灵活,可以手动控制:

    import sysnums = [1, 2, 3, 4]it = iter(nums)whileTrue:try:        print(next(it))   # 每次手动取一个except StopIteration:        sys.exit()        # 取完了就退出

    输出:

    1234

    自己写一个迭代器类

    要让自己的类变成迭代器,需要实现两个魔术方法:

    方法
    作用
    __iter__(self)
    返回迭代器自身(通常 return self)
    __next__(self)
    返回下一个值;没有更多值时抛出 StopIteration
    classMyCounter:"""一个从1开始、无限计数的迭代器"""def__iter__(self):        self.current = 1# 初始化计数器return self           # 返回自身作为迭代器def__next__(self):        value = self.current  # 记录当前值        self.current += 1# 指针前进return value          # 返回当前值counter = MyCounter()it = iter(counter)print(next(it))   # 1print(next(it))   # 2print(next(it))   # 3print(next(it))   # 4print(next(it))   # 5

    注意: 上面这个迭代器会无限计数,不会自动停止。下面介绍如何加停止条件。


    StopIteration:何时停下来

    StopIteration 是 Python 用来通知"迭代结束"的信号。在 __next__ 里,当达到终止条件时手动 raise 它:

    classCountTo:"""从1数到 max_val 后停止"""def__iter__(self):        self.current = 1return selfdef__next__(self):if self.current <= self.max_val:   # 还没数完            value = self.current            self.current += 1return valueelse:raise StopIteration            # 数完了,发出结束信号def__init__(self, max_val):        self.max_val = max_val# 数到5就停for x in CountTo(5):    print(x, end=" ")# 输出: 1 2 3 4 5

    执行流程如下:

    调用 next()    ↓current <= max_val?    ├── 是 → 返回 current,current += 1    └── 否 → raise StopIteration → for 循环感知到,自动退出

    生成器

    yield 关键字

    生成器是用 yield 语句定义的特殊函数。它比手写迭代器类要简洁得多。

    普通函数 vs 生成器函数的区别:

    # 普通函数:一次性返回所有结果(占用大量内存)defget_numbers(n):    result = []for i in range(n):        result.append(i)return result            # 一口气全返回# 生成器函数:每次只产生一个值(按需生成,省内存)defgen_numbers(n):for i in range(n):yield i              # 每次暂停在这里,返回一个值

    yield 的执行逻辑:

    1. 调用生成器函数 → 返回一个生成器对象(并不立即执行函数体)
    2. 每次调用 next() → 函数从上次 yield 的地方继续执行
    3. 遇到 yield → 暂停执行,把 yield 后面的值返回给调用者
    4. 函数执行完毕 → 自动抛出 StopIteration
    defcountdown(n):    print(f"开始从 {n} 倒数")while n > 0:yield n           # ← 每次在这里暂停,把 n 返回出去        n -= 1# ← 下次调用 next() 时从这里继续    print("倒数结束!")gen = countdown(5)        # 创建生成器对象(函数体还没运行)print(next(gen))          # 输出: 开始从5倒数 \n 5print(next(gen))          # 输出: 4print(next(gen))          # 输出: 3for val in gen:           # 继续从4开始遍历剩余的值    print(val)            # 输出: 2  1  倒数结束!

    输出:

    开始从 5 倒数54321倒数结束!

    生成器执行流程图

    下面是 yield 如何让函数"走走停停"的示意图:

    调用 countdown(5)      │      ▼  返回生成器对象 gen(函数体未执行)      │      ▼  next(gen) ──→ 函数开始执行 ──→ 遇到 yield 5 ──→ 暂停,返回 5                                                        │  next(gen) ←───────────────── 从 yield 处继续 ←────────┘      │      ▼  执行 n -= 1 → n=4 → 遇到 yield 4 → 暂停,返回 4      │     ...(重复以上步骤)...      │      ▼  n=0,while 条件不满足 → 函数执行完毕 → 自动抛出 StopIteration

    实战:斐波那契数列

    用生成器实现斐波那契数列,非常优雅:

    deffibonacci(n):"""生成斐波那契数列的前 n+1 项"""    a, b = 0, 1# a 是当前值,b 是下一个值    count = 0while count <= n:yield a               # 产出当前的斐波那契数        a, b = b, a + b       # 更新:a 移到 b,b 变成 a+b        count += 1# 打印前11项(索引0~10)for num in fibonacci(10):    print(num, end=" ")

    输出:

    0 1 1 2 3 5 8 13 21 34 55

    斐波那契数列生成过程(前5步):

    步骤
    当前输出(a)
    下一轮 a
    下一轮 b
    1
    0
    1
    1
    2
    1
    1
    2
    3
    1
    2
    3
    4
    2
    3
    5
    5
    3
    5
    8

    迭代器 vs 生成器:对比总结

    特性
    迭代器(Iterator Class)
    生成器(Generator)
    定义方式
    定义类,实现 __iter__ 和 __next__
    使用 yield 的普通函数
    代码量
    较多,需要手动管理状态
    极少,状态自动保存
    可读性
    中等
    高,逻辑清晰
    内存占用
    视实现而定
    按需生成,极省内存
    适合场景
    复杂的迭代逻辑、面向对象设计
    大数据流、无限序列、流式处理
    本质关系
    生成器是迭代器的一种
    生成器自动实现了迭代器协议

    一句话总结: 生成器是写迭代器的"快捷方式",代码更短,内存更省,优先选择。


    什么时候用生成器?

    场景1:处理超大数据,避免内存溢出

    # ❌ 普通列表:会把100万个数全部载入内存big_list = [x * 2for x in range(1_000_000)]# ✅ 生成器:每次只生成一个,内存占用几乎为零defdouble_gen(n):for x in range(n):yield x * 2for val in double_gen(1_000_000):    process(val)   # 边生成边处理

    场景2:无限序列

    defnatural_numbers():"""无限自然数序列"""    n = 0whileTrue:       # 永不停止yield n        n += 1gen = natural_numbers()print(next(gen))   # 0print(next(gen))   # 1print(next(gen))   # 2# 按需取,永远不会"造完"

    场景3:生成器表达式(列表推导式的懒惰版)

    # 列表推导式:立即生成所有结果squares_list = [x**2for x in range(10)]# 生成器表达式:只在用到时才计算(把 [] 换成 ())squares_gen = (x**2for x in range(10))print(list(squares_gen))   # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]

    总结一下学习路径:

    1. 理解"可迭代对象"→ 能用 for 遍历的东西
    2. 理解"迭代器"→ 有 __iter__ 和 __next__ 的对象
    3. 理解"yield"→ 让函数能暂停和继续
    4. 掌握生成器 → 更优雅地写迭代器
    5. 实战应用 → 大文件读取、流式处理、无限序列

    最新文章

    随机文章