文章目录
-
- 前言
- 一、先搞懂:什么是生成器?为什么要用它?
-
- [1.1 从一个最扎心的场景说起](#1.1 从一个最扎心的场景说起)
- [1.2 生成器的官方定义(2026最新Python规范)](#1.2 生成器的官方定义(2026最新Python规范))
- [二、yield 与 return 的本质区别:一句话戳穿](#二、yield 与 return 的本质区别:一句话戳穿)
-
- [2.1 return:彻底结束,一去不返](#2.1 return:彻底结束,一去不返)
- [2.2 yield:暂停执行,暂存状态](#2.2 yield:暂停执行,暂存状态)
- 三、手写第一个生成器:从代码看执行流程
-
- [3.1 最简单的生成器函数](#3.1 最简单的生成器函数)
- [3.2 调用生成器函数并不会执行代码!](#3.2 调用生成器函数并不会执行代码!)
- [3.3 一步步执行next()](#3.3 一步步执行next())
- 四、生成器核心原理:底层到底发生了什么?
-
- [4.1 生成器 = 函数 + 状态机](#4.1 生成器 = 函数 + 状态机)
- [4.2 为什么生成器极省内存?](#4.2 为什么生成器极省内存?)
- [五、生成器的两种写法:函数式 & 表达式式](#五、生成器的两种写法:函数式 & 表达式式)
-
- [5.1 生成器函数(带yield的函数)](#5.1 生成器函数(带yield的函数))
- [5.2 生成器表达式(2026最常用简写)](#5.2 生成器表达式(2026最常用简写))
- 六、实战:生成器最常用的4大场景(2026最新实战)
-
- [6.1 超大文件读取(必掌握)](#6.1 超大文件读取(必掌握))
- [6.2 无限序列生成(不爆内存)](#6.2 无限序列生成(不爆内存))
- [6.3 数据流水线/管道(Pythonic核心)](#6.3 数据流水线/管道(Pythonic核心))
- [6.4 协程基础(asyncio核心前身)](#6.4 协程基础(asyncio核心前身))
- 七、生成器高级特性:send、close、throw(2026面试高频)
-
- [7.1 send():向生成器传值](#7.1 send():向生成器传值)
- [7.2 close():手动关闭生成器](#7.2 close():手动关闭生成器)
- [7.3 throw():向生成器抛异常](#7.3 throw():向生成器抛异常)
- [八、yield from:2026必备简化语法](#八、yield from:2026必备简化语法)
-
- [8.1 不用yield from的嵌套](#8.1 不用yield from的嵌套)
- [8.2 用yield from极简写法](#8.2 用yield from极简写法)
- 九、常见误区与避坑指南(2026实测)
-
- [9.1 生成器只能迭代一次!](#9.1 生成器只能迭代一次!)
- [9.2 不要在生成器里做耗时阻塞操作](#9.2 不要在生成器里做耗时阻塞操作)
- [9.3 生成器不是线程安全](#9.3 生成器不是线程安全)
- [十、总结:yield 核心原理一句话吃透](#十、总结:yield 核心原理一句话吃透)
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,传送门https://blog.csdn.net/HHX_01
前言
在Python开发领域,2026年的今天,生成器(Generator)早已不是什么小众高级语法,而是成为了Pythonic编程的核心基石之一。无论是日常数据处理、海量文件读取,还是异步编程、AI数据流水线,yield关键字都无处不在。但对于很多刚入门Python的小白,甚至有一两年开发经验的同学来说,yield始终像一层窗户纸------看着简单,一用就懵,到底它和普通return有啥区别?生成器凭什么能省内存?迭代的时候到底发生了什么?
这篇文章就用最通俗的段子+类比,从零拆解Python生成器的核心原理,全程基于Python 3.12+最新特性,2026年通用实战写法,不讲过时内容,不讲虚的,看完你能彻底吃透yield,写出更优雅、更高效的Python代码。
一、先搞懂:什么是生成器?为什么要用它?
1.1 从一个最扎心的场景说起
假设你现在要处理1亿条数据,可能是日志、用户行为、AI训练样本,你第一反应会怎么写?
大概率是这样:
python
def get_big_data():
data = []
for i in range(100000000):
data.append(i)
return data
data_list = get_big_data()
跑一下你就会发现:内存直接炸了。
因为这个函数会一次性把1亿个整数全部塞进列表,全部加载到内存里。你的电脑就算32G内存,也顶不住这种粗暴操作。
这就是普通函数 的痛点:一次性产出所有结果,占满内存。
那生成器呢?
生成器的理念是:我不一次性给你全部,你要一个,我给你一个,用完再说下一个。
就像奶茶店卖奶茶:
- 普通函数:一次性做好1000杯,堆在店里,占满地方,你要喝自己拿。
- 生成器:你点一杯,我做一杯,你喝完再点下一杯,店里永远只留一杯。
内存占用天差地别。
1.2 生成器的官方定义(2026最新Python规范)
根据Python 3.13官方文档定义:
生成器是一种简化的迭代器实现,使用
yield关键字定义,可以暂停执行并保存当前执行状态,下次调用时从暂停处恢复执行。
翻译成人话:
- 生成器本质是迭代器
- 靠
yield暂停函数 - 状态会被保存,下次接着跑
- 不一次性生成所有数据
这就是生成器的核心价值:惰性计算(Lazy Evaluation),用到才计算,极大节省内存。
在2026年AI大模型数据处理、流式数据传输、高并发后端开发中,生成器是标配技能,不会yield,基本写不出高性能Python代码。
二、yield 与 return 的本质区别:一句话戳穿
很多小白最大的困惑:yield不就是return吗?为什么还要多一个关键字?
大错特错!
2.1 return:彻底结束,一去不返
普通函数遇到return:
- 返回值
- 函数彻底结束
- 局部变量全部销毁
- 下次调用从头再来
就像射箭:箭射出去,弓就空了,想再射必须重新搭箭。
2.2 yield:暂停执行,暂存状态
生成器函数遇到yield:
- 返回值
- 函数暂停执行,不结束
- 保存当前所有变量、执行位置、栈状态
- 下次调用
next()时,从yield下一行继续执行
就像游戏机存档:打到boss存个档,下次开机直接从boss开打,不用从头打。
这就是两者最本质的区别:return是终结,yield是暂停。
三、手写第一个生成器:从代码看执行流程
我们用最简单的代码,一步步看yield到底怎么跑。
3.1 最简单的生成器函数
python
def simple_generator():
print("第一步:开始执行")
yield 1
print("第二步:恢复执行")
yield 2
print("第三步:最后执行")
yield 3
注意:这不是普通函数,这是生成器函数。
3.2 调用生成器函数并不会执行代码!
小白最容易踩坑的点:
python
gen = simple_generator()
print(gen)
# 输出:
你会发现:一句print都没执行!
原因:调用生成器函数,只返回生成器对象,不执行函数体 。
函数真正执行,是在第一次调用next()时。
3.3 一步步执行next()
python
gen = simple_generator()
# 第一次next
res1 = next(gen)
print("返回值:", res1)
# 输出:
# 第一步:开始执行
# 返回值: 1
# 第二次next
res2 = next(gen)
print("返回值:", res2)
# 输出:
# 第二步:恢复执行
# 返回值: 2
# 第三次next
res3 = next(gen)
print("返回值:", res3)
# 输出:
# 第三步:最后执行
# 返回值: 3
# 第四次next
next(gen)
# 抛出 StopIteration 异常
执行流程总结:
- 第一次
next():从头跑到第一个yield,暂停,返回1 - 第二次
next():从第一个yield下一行跑到第二个yield,暂停,返回2 - 第三次
next():从第二个yield下一行跑到第三个yield,暂停,返回3 - 第四次
next():函数跑完,无yield可执行,抛出StopIteration,迭代结束
这就是yield的完整生命周期:运行-暂停-恢复-暂停-...-结束。
四、生成器核心原理:底层到底发生了什么?
2026年很多面试都会问:生成器底层是怎么实现状态保存的?
不用背八股,用通俗逻辑就能理解。
4.1 生成器 = 函数 + 状态机
从底层看,生成器对象内部维护了三个关键东西:
- 指令指针(Instruction Pointer):记录上次停在哪一行
- 局部变量栈:保存所有变量值,不释放
- 运行状态:GEN_SUSPENDED(暂停)、GEN_RUNNING(运行)、GEN_CLOSED(关闭)
普通函数:调用->运行->结束->销毁栈
生成器函数:调用->创建对象->next->运行->暂停->保存栈->next->恢复栈->继续运行
4.2 为什么生成器极省内存?
普通列表:
python
# 1亿个整数,大约占用400MB+内存(2026实测)
a = [i for i in range(100_000_000)]
生成器:
python
# 永远只保存当前状态,内存占用几乎不变(KB级别)
g = (i for i in range(100_000_000))
因为生成器不存储全部结果,只存储计算规则和当前状态,每next一次,计算一次值,用完即丢(除非你手动存)。
在2026年大模型训练数据加载、流式日志处理中,这一点是决定性优势。
五、生成器的两种写法:函数式 & 表达式式
5.1 生成器函数(带yield的函数)
就是我们上面写的:
python
def gen_func():
yield 1
yield 2
适用场景:复杂逻辑、循环嵌套、文件读取、数据处理流水线。
5.2 生成器表达式(2026最常用简写)
类似列表推导式,把[]换成():
python
gen = (x * 2 for x in range(1000))
极简、优雅,适合简单逻辑。
注意区分:
[]:列表推导式,立即创建所有元素():生成器表达式,惰性生成
六、实战:生成器最常用的4大场景(2026最新实战)
6.1 超大文件读取(必掌握)
处理10GB、100GB日志文件,不能一次性读进内存:
python
def read_large_file(file_path):
with open(file_path, 'r', encoding='utf-8') as f:
for line in f:
yield line.strip()
# 逐行读取,内存永远只存一行
for line in read_large_file("big_log.log"):
process(line)
这是2026年后端、数据开发必会写法。
6.2 无限序列生成(不爆内存)
python
def infinite_counter():
num = 0
while True:
yield num
num += 1
# 想取多少取多少,不会死循环爆内存
gen = infinite_counter()
print(next(gen)) # 0
print(next(gen)) # 1
print(next(gen)) # 2
用于ID生成、数据流、AI样本生成等场景。
6.3 数据流水线/管道(Pythonic核心)
多个生成器串联,形成数据流:
python
def num_gen():
for i in range(10):
yield i
def square_gen(nums):
for num in nums:
yield num ** 2
def even_gen(nums):
for num in nums:
if num % 2 == 0:
yield num
# 流水线:原始数 -> 平方 -> 过滤偶数
pipeline = even_gen(square_gen(num_gen()))
for res in pipeline:
print(res)
无中间列表,全程流式处理,内存极低。
6.4 协程基础(asyncio核心前身)
2026年异步编程遍地都是,而async/await底层就是基于生成器原理 。
yield可以实现任务切换,是协程的基石。
简单演示:
python
def task1():
yield "任务1暂停"
yield "任务1恢复"
def task2():
yield "任务2暂停"
yield "任务2恢复"
g1 = task1()
g2 = task2()
next(g1) # 切换任务1
next(g2) # 切换任务2
next(g1) # 切回任务1
这就是协程"微线程"切换的核心思想。
七、生成器高级特性:send、close、throw(2026面试高频)
7.1 send():向生成器传值
yield不仅能返回值,还能接收外部传入的值:
python
def generator_with_send():
val = yield "开始"
print("接收外部值:", val)
yield "结束"
gen = generator_with_send()
print(next(gen)) # 输出:开始
print(gen.send("666")) # 传入666,输出:结束
执行逻辑:
send(value)会恢复生成器,并把value赋给yield表达式- 第一次必须用
next(),不能直接send
7.2 close():手动关闭生成器
关闭后,生成器直接进入GEN_CLOSED状态:
python
gen = (i for i in range(10))
gen.close()
next(gen) # StopIteration
用于资源释放、中断数据流。
7.3 throw():向生成器抛异常
python
def gen():
try:
yield 1
except ValueError:
print("捕获到异常")
g = gen()
next(g)
g.throw(ValueError)
用于错误控制、数据流中断。
八、yield from:2026必备简化语法
yield from是Python 3.3+引入,2026年已是标准写法,用于委托生成器。
8.1 不用yield from的嵌套
python
def nested_gen():
for i in [1,2,3]:
yield i
def main_gen():
for i in nested_gen():
yield i
8.2 用yield from极简写法
python
def main_gen():
yield from [1,2,3]
yield from可以:
- 直接迭代子生成器
- 自动处理
StopIteration - 支持双向传值(send穿透)
在多层生成器嵌套、异步生成器中必不可少。
九、常见误区与避坑指南(2026实测)
9.1 生成器只能迭代一次!
python
gen = (i for i in range(3))
print(list(gen)) # [0,1,2]
print(list(gen)) # [] 空了!
生成器是一次性迭代器,跑完就废,想复用必须重新创建。
9.2 不要在生成器里做耗时阻塞操作
虽然省内存,但如果yield内部有阻塞IO,依然会拖慢整个流程,2026年推荐搭配异步生成器async def + async for。
9.3 生成器不是线程安全
多线程同时操作同一个生成器会导致状态混乱,必须加锁。
十、总结:yield 核心原理一句话吃透
最后用最精炼的话总结yield与生成器:
- yield = 函数暂停 + 状态保存 + 返回值
- 生成器 = 惰性迭代器 + 状态机 + 内存优化
- 执行流程:创建→next运行→yield暂停→next恢复→循环直至结束
- 核心价值:处理海量数据、流式IO、协程基石、Pythonic编程
2026年的今天,yield早已不是高级技巧,而是Python开发者的基本功。无论是AI开发、后端高并发、数据处理,学会yield,你的代码会更高效、更优雅、更符合现代Python工程规范。
P.S. 无意间发现了一个巨牛的人工智能教程,非常通俗易懂,对AI感兴趣的朋友强烈推荐去看看,传送门https://blog.csdn.net/HHX_01