Python 中的迭代器与生成器详解:原理、实现与实战

在 Python 中,迭代器(Iterator)和生成器(Generator)是实现惰性计算、节省内存、优化性能的核心工具。

一、什么是迭代器(Iterator)?

1.1 基本定义

迭代器是一种对象,它实现了 Python 的迭代协议,包含两个方法:

  • __iter__():返回迭代器本身。
  • __next__():返回下一个值,如果没有更多元素,抛出 StopIteration 异常。

1.2 迭代器的特点

  • 惰性计算:不会一次性加载所有数据,而是按需生成,适用于数据量大的场景。
  • 单向性:只能向前迭代,不能回退。
  • 节省内存:无需保存整个序列在内存中。

1.3 创建迭代器对象

我们可以通过内置函数 iter() 将可迭代对象转换为迭代器:

L = [1, 3, 5, 7]
it = iter(L)       # 获取迭代器对象
print(next(it))    # 输出 1
print(next(it))    # 输出 3

解释: iter(L) 会返回一个迭代器,该对象能逐个读取列表的元素。每次调用 next() 都会返回下一个值,直到抛出 StopIteration


二、自定义迭代器类

当你需要完全控制迭代逻辑时,可以通过定义一个类手动实现 __iter__()__next__()

class MyIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0

    def __iter__(self):
        return self  # 返回自身作为迭代器

    def __next__(self):
        if self.index < len(self.data):
            result = self.data[self.index]
            self.index += 1
            return result
        else:
            raise StopIteration

# 使用自定义迭代器
my_iter = MyIterator([10, 20, 30])
for item in my_iter:
    print(item)

解释: MyIterator 能逐个输出列表中的元素,每次迭代内部索引递增。当超出范围时,会自动终止迭代。


三、什么是生成器(Generator)?

3.1 基本定义

生成器是使用 yield 的函数或表达式,具有迭代器所有功能,同时具有更简洁的语法。

3.2 生成器函数的特点

  • 使用 yield 替代 return
  • 每次调用 next(),从上一次 yield 处恢复执行。
  • 自动实现 __iter__()__next__(),无需显式定义。

3.3 生成器函数示例

def my_generator():
    print("Start")
    yield 1
    print("Continue")
    yield 2
    print("End")
    yield 3

gen = my_generator()
for val in gen:
    print(val)

输出解释:

Start
1
Continue
2
End
3

每次遇到 yield,生成器暂停并返回值;下一次迭代会从 yield 的下一行继续执行。


四、生成器表达式

Python 提供类似列表推导式的语法,用小括号表示生成器表达式:

gen_expr = (x**2 for x in range(5))
print(next(gen_expr))  # 0
print(next(gen_expr))  # 1

解释:这种方式生成一个迭代器对象,按需计算每个值,节省内存,适用于大数据处理。


五、经典实战示例

5.1 斐波那契数列生成器

def fibonacci(n):
    a, b = 0, 1
    for _ in range(n):
        yield b
        a, b = b, a + b

for num in fibonacci(10):
    print(num, end=' ')

输出:

1 1 2 3 5 8 13 21 34 55

解释: 使用 yield 保持状态,可以轻松生成大量数据。


5.2 倒数生成器

def Descendorder(n):
    while n > 0:
        yield n
        n -= 1

gen = Descendorder(5)
print(next(gen))  # 5
print(next(gen))  # 4
for i in gen:
    print('for循环:', i)

输出:

5
4
for循环: 3
for循环: 2
for循环: 1

5.3 流式读取大文件

def read_large_file(file_path):
    with open(file_path, 'r') as file:
        for line in file:
            yield line.strip()

# 假设文件很大,逐行读取避免内存爆炸
for line in read_large_file('large_file.txt'):
    print(line)

5.4 图像标签生成器:结合迭代器与 PIL 处理图像元数据

import os
from PIL import Image
print("当前工作目录:", os.getcwd())

class DataLoader:

    def __init__(self, filelist):
        self.filelist = filelist
        self.index = 0

    def __iter__(self):
        return self

    def __next__(self):
        if self.index < len(self.filelist):
            p = self.filelist[self.index]
            self.index += 1

            # 解析图像文件名中的坐标数据
            axis = tuple([tuple(map(int, a.split('&'))) for a in p.split('-')[2].split('_')])

            # 计算车牌区域的宽和高
            width = axis[1][0] - axis[0][0]
            height = axis[1][1] - axis[0][1]

            # 计算中心点坐标
            centerX = (axis[0][0] + axis[1][0]) / 2
            centerY = (axis[0][1] + axis[1][1]) / 2

            # 归一化到图像大小范围(0~1)
            readImg = Image.open('./imagestest/' + p)
            size = readImg.size
            width /= size[0]
            height /= size[1]
            centerX /= size[0]
            centerY /= size[1]

            # 构造标签字符串
            label = f'{centerX} {centerY} {width} {height}'

            return label, p.split('.')[0]
        else:
            raise StopIteration

# 获取图像文件列表并处理
filelist = os.listdir('./imagestest')
datasets = DataLoader(filelist)

for label, filename in datasets:
    print(label)
    with open(f'./labels/{filename}.txt', mode='w', encoding='utf-8') as f:
        f.write(label)
    print(f'{filename} 处理done...')

解释:该例展示了如何将图像处理与迭代器结合,通过文件名提取标签并写入目标路径,是图像标注和机器学习训练准备的常见任务。


六、应用场景总结

场景 推荐方式 原因
自定义复杂遍历逻辑 迭代器 精细控制状态
简单或数据流生成 生成器 写法简单,自动管理状态
处理大文件/数据管道 生成器 惰性加载,节省内存
无限序列,如斐波那契、素数 生成器 延迟计算,避免爆内存

七、迭代器 vs 生成器:区别对比表

特性 迭代器 生成器
实现方式 通过类实现 __iter__ + __next__ 通过函数 + yield 实现
代码复杂度 相对复杂 非常简洁
状态管理 手动管理 自动保存中断状态
性能 高效 更高效(尤其是大数据)
内存占用 更低

八、小结

  • 迭代器更适合需要完全控制迭代过程的场景,灵活但代码冗长。
  • 生成器适合快速实现可迭代对象,尤其在数据量大时节省资源。
  • 生成器表达式提供更加紧凑的写法,是列表推导式的高效替代。
Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐