Python迭代器与生成器
Python迭代器与生成器
三层核心概念关系
1. 可迭代对象(Iterable)
定义:凡是可以被 for 循环遍历的对象。
特征:内部实现了 __iter__() 方法。
常见对象:list、tuple、str、dict、set、文件对象。
特点:可重复遍历,不具备惰性取值能力。
2. 迭代器(Iterator)
定义:可迭代对象的子集,是可以逐个取值的惰性数据流。
必备双方法(核心标准):
__iter__():返回自身__next__():返回下一个元素,无元素时抛出StopIteration异常
三大核心特性:
惰性加载:用一个取一个,不占用大量内存
单向不可逆:只能从头向后遍历,无法回头
一次性消耗:遍历完毕后彻底为空,无法二次使用
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/表达式) |
|---|---|---|
| 代码复杂度 | 高,需手动实现双方法、抛异常 | 极低,极简语法自动实现 |
| 底层本质 | 原生迭代器 | 迭代器的语法糖,本质仍是迭代器 |
| 适用场景 | 复杂迭代逻辑、需自定义状态管理 | 常规流式数据、大数据遍历、简单迭代 |
| 核心优势 | 灵活度高、可高度自定义 | 开发高效、代码简洁、省内存 |
最终总结
层级关系:可迭代对象 ⊃ 迭代器 ⊃ 生成器(生成器是特殊的迭代器)
核心优势:惰性求值,无需一次性加载全部数据,极致节省内存,适配海量数据场景
核心局限:单向遍历、一次性消耗,不支持索引、切片、重复遍历
使用场景:超大文件读取、无限序列生成、爬虫分页数据、大数据批量计算
选型原则:简单迭代用生成器,复杂自定义迭代逻辑用类迭代器
版权声明
本文为 程序员青阳 原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文链接及本声明。