Python迭代器与生成器

Python迭代器与生成器

三层核心概念关系

1. 可迭代对象(Iterable)

定义:凡是可以被 for 循环遍历的对象。

特征:内部实现了 __iter__() 方法。

常见对象:list、tuple、str、dict、set、文件对象。

特点:可重复遍历,不具备惰性取值能力。

2. 迭代器(Iterator)

定义:可迭代对象的子集,是可以逐个取值的惰性数据流。

必备双方法(核心标准)

  • __iter__():返回自身

  • __next__():返回下一个元素,无元素时抛出 StopIteration 异常

三大核心特性:

  1. 惰性加载:用一个取一个,不占用大量内存

  2. 单向不可逆只能从头向后遍历,无法回头

  3. 一次性消耗遍历完毕后彻底为空,无法二次使用

3. 生成器(Generator)

本质简化版的迭代器,语法更简洁,无需手动实现迭代器双方法、无需手动抛异常。

核心作用:用极简代码快速创建迭代器,保留迭代器所有优势。

实战案例

案例1:自定义迭代器(类实现)

适用于复杂迭代逻辑,手动管控迭代状态:

class Counter:
    def __init__(self, max_num):
        """
        初始化计数器
        :param max_num: 计数的最大值
        """
        self.max = max_num  # 设置计数器的最大值
        self.current = 0    # 初始化当前计数值为0

    # 迭代器必须返回自身
    def __iter__(self):
        """
        迭代器方法,返回迭代器对象自身
        :return: 迭代器对象自身
        """
        return self

    # 实现取值逻辑
    def __next__(self):
        """
        获取下一个计数值
        :return: 当前计数值加1
        :raises StopIteration: 当当前计数值达到最大值时,抛出停止迭代异常
        """
        if self.current < self.max:
            self.current += 1
            return self.current
        # 遍历结束,终止迭代
        raise StopIteration

def demo1():
    c = Counter(3)
    # print(c) # 打印内存地址
    print(list(c))  # [1, 2, 3]

def demo2():
    # 创建一个计数器对象,初始值为3
    c = Counter(3)
    # 遍历计数器对象,每次迭代将计数器减1,直到计数器为0
    for i in c:
        print(i)

def demo3():

    """
    演示Counter类的使用,创建一个初始值为3的计数器,
    然后通过next()函数依次获取计数器的值。
    """
    c = Counter(3)  # 创建一个初始值为3的Counter对象
    print(next(c))  # 1 - 第一次调用next(),返回1
    print(next(c))  # 2 - 第二次调用next(),返回2
    print(next(c))  # 3 - 第三次调用next(),返回3
    print(next(c))  # 报错 - 第四次调用next(),计数器耗尽

if __name__ == '__main__':
    # demo1()
    # demo2()
    demo3()

案例2:生成器函数(yield 核心用法)

yield 核心特性:暂停函数执行、保留函数上下文状态、下次取值从暂停处继续执行(区别于return直接结束函数)。

def counter_gen(max_num):
    """
    生成器函数,用于生成从1到max_num的数字序列
    参数:
        max_num (int): 计数器的最大值,生成器将生成1到max_num的数字
    返回:
        generator: 一个生成器对象,每次调用生成一个递增的数字
    """
    current = 0  # 初始化当前计数器为0
    while current < max_num:  # 当当前计数小于最大值时继续循环
        current += 1  # 计数器加1
        yield current  # 暂停并返回值,保存现场

def demo1():
    # 创建一个生成器对象,生成3个数字
    gen = counter_gen(3)
    # 将生成器转换为列表并打印
    print(list(gen))


def demo2():
    # 创建一个生成器对象gen,调用counter_gen函数并传入参数3
    gen = counter_gen(3)
    # 遍历生成器gen中的每个元素
    for i in gen:
        # 打印生成器中的当前元素i
        print(i)

def demo3():
    # 创建一个计数器生成器,参数为3,表示生成1到3的数字
    gen = counter_gen(3)
    print(next(gen))  # 使用next()函数获取生成器的下一个值,输出1
    print(next(gen))  # 再次使用next()函数获取生成器的下一个值,输出2
    print(next(gen))  # 最后一次使用next()函数获取生成器的下一个值,输出3
    print(next(gen))  # 报错

if __name__ == '__main__':
    # demo1()
    # demo2()
    demo3()

案例3:生成器表达式(极简写法)

与列表推导式对应,[](),惰性取值、不预占内存:

# 列表推导式:一次性加载所有数据,占用内存
list_data = [x * 2 for x in range(10000)]
print(type(list_data))  # <class 'list'>
print(list_data)  # 0 2 4 ...

# 生成器表达式:按需取值,极致省内存
gen_data = (x * 2 for x in range(10000))
print(type(gen_data))  # <class 'generator'>
print(next(gen_data))  # 0
print(next(gen_data))  # 2

案例4:实战场景:超大文件逐行读取

解决大文件读取内存溢出问题,工业级常用写法:

def read_large_file(file_path):
    with open(file_path, "r", encoding="utf-8") as f:
        # 文件对象本身是迭代器,逐行读取不加载全文
        for line in f:
            yield line.strip()

案例5:无限序列生成(斐波那契数列)

迭代器/生成器可实现无限数据流,列表无法实现:

def fib_gen():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

# 无限取值,按需获取
fib = fib_gen()
print(next(fib)) # 0
print(next(fib)) # 1
print(next(fib)) # 1
print(next(fib)) # 2

案例总结

自定义迭代器步骤总结

__iter__ → 返回 self(迭代器必须返回自身)

__next__ → 实现取值逻辑

StopIteration → 终止迭代的标志

api总结

next(迭代器/生成器对象) → 拿下一个值,用完抛 StopIteration

for i in 迭代器/生成器对象: → 自动遍历,直到结束

list(迭代器/生成器对象) → 一次性全部取出(消耗迭代器/生成器)

核心进阶知识点

1. yield 与 return 区别

  • return:终止函数、销毁局部变量、直接返回结果

  • yield:暂停函数、保留变量状态、下次继续执行、返回迭代值

2. yield from 简化嵌套迭代

替代多层for循环,简化子迭代器遍历逻辑:

def sub_gen():
    yield 1
    yield 2

def main_gen():
    yield from sub_gen() # 直接遍历子生成器
    yield 3

print(list(main_gen())) # [1, 2, 3]

迭代器 vs 生成器 对比总结

对比维度自定义迭代器(类)生成器(yield/表达式)
代码复杂度高,需手动实现双方法、抛异常极低,极简语法自动实现
底层本质原生迭代器迭代器的语法糖,本质仍是迭代器
适用场景复杂迭代逻辑、需自定义状态管理常规流式数据、大数据遍历、简单迭代
核心优势灵活度高、可高度自定义开发高效、代码简洁、省内存

最终总结

  1. 层级关系:可迭代对象 ⊃ 迭代器 ⊃ 生成器(生成器是特殊的迭代器)

  2. 核心优势:惰性求值,无需一次性加载全部数据,极致节省内存,适配海量数据场景

  3. 核心局限单向遍历、一次性消耗,不支持索引、切片、重复遍历

  4. 使用场景:超大文件读取、无限序列生成、爬虫分页数据、大数据批量计算

  5. 选型原则:简单迭代用生成器,复杂自定义迭代逻辑用类迭代器

本文为 程序员青阳 原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文链接及本声明。

原文链接:https://heliufang.github.io/posts/478ace0d/index.html