
前言日常写 Python 代码时for循环绝对是出镜率最高的语法之一。遍历列表、字符串、字典、文件内容我们都会习惯性写下循环语句简简单单几行代码就能逐个取出容器内的数据。nums [1, 2, 3, 4, 5] for num in nums: print(num)运行结果依次打印出五个数字整个过程直观又易懂。但很多开发者只知用法不懂底层原理为什么列表、字符串这类数据结构都能被for遍历循环到底是如何逐个获取元素的iter()与next()内置函数承担着怎样的作用自定义类如何实现循环遍历yield关键字又是凭什么诞生生成器还能高效处理海量数据这些问题的核心答案全都指向 Python 底层的迭代机制。迭代器是循环运行的底层支撑生成器则是迭代器的简化升级版。本篇文章由浅入深从基础 for 循环逐层拆解区分可迭代对象与迭代器手把手实现自定义迭代器再延伸讲解 yield 生成器理清四者之间的关联与差异零基础也能全面掌握这套核心语法。目录for 循环底层真身迭代器驱动遍历核心概念区分可迭代对象 VS 迭代器手动编写自定义迭代器吃透迭代协议迭代器使用痛点引出生成器解决方案yield 生成器原理极简写法替代迭代器生成器核心优势惰性取值节省内存迭代器与生成器从属关系梳理日常开发高频误区避坑总结全文核心知识点复盘一、for 循环底层真身迭代器驱动遍历我们肉眼看到的 for 循环是封装好的简洁语法其底层并不是直接读取容器元素而是严格遵循迭代器规则完成取值操作。依旧以列表遍历代码为例nums [1, 2, 3, 4, 5] for num in nums: print(num)看似自动遍历取值将代码拆解为底层原生逻辑就能看清真实执行流程nums [1, 2, 3, 4, 5] # 1. 将可迭代对象转为迭代器实例 iter_obj iter(nums) # 2. 循环调用next不断获取元素 while True: try: next_num next(iter_obj) print(next_num) # 3. 捕获迭代结束异常终止循环 except StopIteration: break两段代码运行效果完全一致这里出现两个至关重要的内置函数iter()接收可迭代对象生成对应的迭代器实例next()从迭代器中提取下一个元素当迭代器内部所有数据全部取出后继续调用next()就会抛出StopIteration终止异常。for 循环内部自带异常捕获逻辑监测到该异常后自动结束遍历不会让程序报错崩溃。由此总结for 循环底层执行逻辑 第一步调用iter()获取迭代器第二步循环调用next()依次取值最终捕获终止异常结束整个遍历流程。二、核心概念区分可迭代对象 VS 迭代器初学迭代相关知识最容易混淆可迭代对象和迭代器两个概念二者属性、作用、判定标准完全不同我们分开拆解理解。2.1 什么是可迭代对象凡是能够被iter()函数处理转换为迭代器的对象都属于可迭代对象。Python 中日常使用的基础容器类型全部都是可迭代对象序列类型列表 list、元组 tuple、字符串 str、范围 range集合映射集合 set、字典 dict判定标准类内部实现了__iter__()魔法方法满足该条件即为合法可迭代对象。基础示例验证# 列表属于可迭代对象 nums [1, 2, 3] # 成功转为迭代器 iter_obj iter(nums)2.2 什么是迭代器迭代器是专门负责逐个产出数据的对象是遍历动作的实际执行者。判定标准必须同时实现两个魔法方法__iter__()固定返回迭代器自身__next__()负责返回下一个元素无数据时抛出StopIteration异常2.3 两者核心分工可迭代对象具备被遍历的资格相当于存放数据的仓库迭代器具备逐个取数的能力相当于仓库的取货工具简单概括先有可迭代对象才能生成迭代器迭代器依托可迭代对象存在真正完成取值遍历。三、手动编写自定义迭代器吃透迭代协议理解理论概念后动手自定义迭代器就能彻底吃透 Python 迭代协议。接下来编写一个迭代器实现依次输出 1~5 的数字。3.1 完整自定义迭代器代码class MyIter: # 初始化定义记录遍历位置的状态变量 def __init__(self): self.count 0 # 迭代器规范方法返回自身 def __iter__(self): return self # 核心取值方法 def __next__(self): self.count 1 # 数值在范围内正常返回数据 if self.count 5: return self.count # 超出范围抛出迭代终止异常 else: raise StopIteration(数据遍历完毕)3.2 直接调用迭代器取值按照底层逻辑使用iter()和next()手动取值my_iter MyIter() my_iter_obj iter(my_iter) num1 next(my_iter_obj) num2 next(my_iter_obj) num3 next(my_iter_obj) print(num1, num2, num3)输出结果1 2 33.3 迭代器核心特性代码中self.count是迭代器的状态标记专门记录当前遍历到的位置。每调用一次next()计数自增并返回对应数值数值超出设定范围后触发终止异常。由此得出迭代器关键特点迭代器自带记忆状态会保留上一次遍历的位置不会重复取值。3.4 for 循环遍历自定义迭代器只要遵循迭代协议的对象都可以直接使用 for 循环遍历自定义迭代器也不例外for num in MyIter(): print(num)运行输出1 2 3 4 5这也再次印证for 循环不区分内置容器还是自定义对象只要满足迭代协议就能正常遍历执行。四、迭代器使用痛点引出生成器解决方案手动编写迭代器虽然逻辑清晰但实际开发中存在明显弊端。回看上面的自定义迭代器代码为了实现简单的数字遍历需要创建类结构、定义状态变量、编写两个魔法方法还要手动抛出终止异常代码冗余繁琐。class MyIter: def __init__(self): self.count 0 def __iter__(self): return self def __next__(self): self.count 1 if self.count 5: return self.count else: raise StopIteration频繁编写这类模板化代码效率极低Python 为此推出了简化方案 ——生成器。生成器本质是迭代器的便捷写法无需手动定义类、魔法方法和状态管理借助yield关键字就能快速创建迭代器。五、yield 生成器原理极简写法替代迭代器生成器依托函数创建核心标识为yield关键字它的执行逻辑和普通函数有着本质区别。5.1 普通函数与生成器函数对比普通函数调用后会立刻执行函数体全部代码def func(): print(开始执行了) func()输出内容直接打印执行流程一气呵成。当函数内部写入yield后函数身份转变为生成器函数def func(): print(开始执行了) yield 1 # 调用函数不会执行代码仅返回生成器对象 generator func()此时调用函数不会运行内部代码只会生成一个生成器实例。只有使用next()取值时函数才会启动执行。5.2 yield 暂停执行特性print(next(generator))执行步骤拆解进入函数内部执行打印语句运行至yield 1处暂停整个函数执行将数值 1 返回给调用方保留当前运行状态yield最核心的特性返回数据的同时冻结函数状态。再次调用next()函数不会从头运行而是从上一次暂停的位置继续向后执行。5.3 实战案例生成随机数序列利用生成器按需产出数据批量生成 100 个 1~100 的随机整数import random def random_generator(): for i in range(100): yield random.randint(1, 100) # 循环遍历生成器取值 for num in random_generator(): print(num)这段代码不会一次性生成 100 个数字存入内存而是遵循取用规则循环需要数据时生成器运行到 yield 产出数值随后暂停等待下一次调用。六、生成器核心优势惰性取值节省内存生成器采用惰性计算模式也叫延迟取值这是它最大的实用价值。普通列表存储海量数据时会一次性将所有元素加载到内存中数据量级越大占用内存越高数据过多还会出现内存溢出问题。而生成器不会提前存储全部数据仅记录生成规则只有代码需要使用元素时才现场计算产出单个数据使用完毕后不会常驻内存。日常开发中读取超大日志文件、批量处理海量爬虫数据、构建无限循环序列等场景优先使用生成器能够极大降低内存消耗提升程序运行稳定性。七、迭代器与生成器从属关系梳理从层级定义上来说生成器是一种特殊的迭代器。生成器对象完全适配迭代器协议支持iter()转换、next()取值操作自动内置状态记忆、迭代终止判定等能力省去开发者手动编写底层方法的工作量。验证代码def nums(): yield 1 yield 2 yield 3 # 获取生成器对象 g nums() # 生成器迭代器自身一致 print(iter(g) is g) # 逐个取值 print(next(g)) print(next(g)) print(next(g))运行结果True 1 2 3结果证明生成器本身就是迭代器是 Python 封装好的轻量化迭代器实现方式。八、日常开发高频误区避坑总结学习迭代器与生成器极易出现概念混淆、执行逻辑判断错误梳理三类最常见误区。误区 1误认为列表就是迭代器列表属于可迭代对象并不等同于迭代器。nums [1, 2, 3] # 拥有可迭代对象方法 print(hasattr(nums, __iter__)) # 不具备迭代器取值方法 print(hasattr(nums, __next__))输出True、False只有通过iter(列表)转换之后得到的实例才是真正的迭代器具备__next__取值能力。误区 2包含 yield 的函数调用即执行带有 yield 的生成器函数调用仅创建生成器对象不会执行内部代码。def func(): print(开始执行了) yield 1 # 无任何打印输出 g func()只有调用next(g)触发取值函数代码才会真正运行。误区 3将 StopIteration 判定为程序报错StopIteration不属于代码 BUG是迭代机制约定的正常结束信号。迭代器无剩余元素时主动抛出该异常告知调用方遍历结束for 循环自动捕获处理日常开发无需手动捕获。九、全文核心知识点复盘通篇从基础循环层层递进梳理 Python 迭代体系核心要点汇总关键结论for 循环底层依托iter()创建迭代器、next()循环取值捕获终止异常结束遍历可迭代对象实现__iter__方法具备遍历资格迭代器同时实现__iter__与__next__负责实际取数迭代器自带状态记忆遍历位置不会错乱手动编写迭代器代码模板繁琐yield 关键字打造生成器是迭代器的简化写法自动遵循迭代协议生成器采用惰性取值按需生成数据海量数据场景下内存占用极低生成器隶属于迭代器范畴简化开发的同时保留迭代器全部特性区分可迭代对象与迭代器规避执行逻辑、属性判定类常见错误。