Python yield 完整用法详解
一、核心概念:yield 生成器(generator)
包含
只有迭代(
yield 的函数不再是普通函数,调用后返回一个生成器对象,不会立刻执行函数内部代码;
next()/for 循环)时,函数才会运行,遇到 yield 暂停,返回值,下次迭代从暂停处继续执行。简单来说,它的作用类似于 return,但最大的区别在于:return 会彻底结束函数并返回一个值,而 yield 只是暂停函数的执行并返回一个值,下次调用时可以从暂停的地方继续执行。
1. 基础用法:生成器函数
只要函数体内包含
yield,这个函数就不再是普通函数,而是一个生成器函数。调用它时,不会立即执行函数体,而是返回一个生成器对象。示例一:
def count_up_to(max_num):
n = 1
while n <= max_num:
yield n # 暂停执行,并返回 n
n += 1 # 下次调用时从这里继续
# 获取生成器对象
counter = count_up_to(3)
print(next(counter)) # 输出: 1 (执行到第一个 yield)
print(next(counter)) # 输出: 2 (从 n += 1 继续,遇到第二个 yield)
print(next(counter)) # 输出: 3
print(next(counter)) # 抛出 StopIteration 异常
示例二:
def num_generator():
yield 1
yield 2
yield 3
# 调用函数,得到生成器,不执行内部代码
g = num_generator()
# 第一种取值:next()
print(next(g)) # 输出1,停在第一个yield
print(next(g)) # 输出2,停在第二个yield
print(next(g)) # 输出3,停在第三个yield
# next(g) 无更多yield,抛出 StopIteration 异常
# 第二种取值:for循环(自动捕获StopIteration)
for i in num_generator():
print(i)
输出结果如下

示例三:带循环的生成器(无限 / 有限序列)
# 生成1~n的平方
def square(n):
for i in range(1, n+1):
yield i ** 2
g = square(5)
print(list(g)) # [1, 4, 9, 16, 25]
输出结果如下

优势:惰性求值,不会一次性把所有数据存入内存,处理超大序列、文件流、爬虫分页极其省内存。
二、send ():向生成器传入数据(双向通信)
yield 可以接收外部通过 send() 传入的值,语法:变量 = yield 返回值yield 不仅能向外输出数据,还能接收外部传入的数据。通过 generator.send(value),可以将 value 作为 yield 表达式的返回值。def accumulator():
total = 0
while True:
# 第一次 next() 走到这里暂停,返回 0
# 后续 send(value) 时,value 会赋给 number
number = yield total
if number is None:
break
total += number
acc = accumulator()
next(acc) # 必须先启动生成器(走到第一个 yield)
print(acc.send(10)) # 输出: 10
print(acc.send(20)) # 输出: 30
print(acc.send(5)) # 输出: 35
关键点:
- 生成器刚创建时,必须先
next()预热到第一个yield,才能send() send(None)等价于next()三、throw ():主动抛出异常
在生成器暂停的yield处抛出指定异常:def gen(): try: yield 1 yield 2 except ValueError: print("捕获到异常") g = gen() next(g) g.throw(ValueError) # 触发异常捕获
输出结果如下

四、close ():关闭生成器
调用后后续迭代会直接抛出
StopIteration,可配合 finally 做资源释放:def read_file():
try:
with open("test.txt", "r", encoding="utf-8") as f:
for line in f:
yield line.strip()
finally:
print("文件资源已释放")
g = read_file()
next(g)
g.close()
五、yield from 简化嵌套生成器
yield from iterable 等价于循环迭代子生成器并逐个 yield,简化多层嵌套代码在 Python 3.3 之前,如果你想在一个生成器中调用另一个生成器(即嵌套),你必须手动写一个
for 循环来 yield 子生成器的每一个值。这不仅代码冗长,而且无法正确处理 send()、throw() 等高级协程方法。yield from 的引入完美解决了这个问题。它不仅能自动遍历子生成器并产出所有值,还能双向透明地传递数据和控制流。在 Python 3.3 之前,如果你想在一个生成器中调用另一个生成器(即嵌套),你必须手动写一个
for 循环来 yield 子生成器的每一个值。这不仅代码冗长,而且无法正确处理 send()、throw() 等高级协程方法。yield from 的引入完美解决了这个问题。它不仅能自动遍历子生成器并产出所有值,还能双向透明地传递数据和控制流。1. 基础对比:告别繁琐的 for 循环
❌ 传统写法(Python 3.3 之前):
def sub_generator():
yield 1
yield 2
def main_generator():
yield 0
# 必须手动遍历子生成器
for value in sub_generator():
yield value
yield 3
✅ 使用
yield from:def main_generator():
yield 0
yield from sub_generator() # 一行搞定,自动展开
yield 3
(注:
yield from 后面可以跟任何可迭代对象,如列表、元组、字符串等,不仅限于生成器。)
六、生成器表达式(简写)
和列表推导式类似,
[] 换成 () 就是生成器,底层用 yield 实现:lst = [x*2 for x in range(10)] # 列表,一次性存全部数据
gen = (x*2 for x in range(10)) # 生成器,惰性计算
print(next(gen))

浙公网安备 33010602011771号