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)

输出结果如下

image

示例三:带循环的生成器(无限 / 有限序列)

# 生成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]

输出结果如下

image

优势:惰性求值,不会一次性把所有数据存入内存,处理超大序列、文件流、爬虫分页极其省内存。

二、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
关键点:
  1. 生成器刚创建时,必须先 next() 预热到第一个 yield,才能 send()
  2. send(None) 等价于 next()

    三、throw ():主动抛出异常

    在生成器暂停的 yield 处抛出指定异常:
    def gen():
        try:
            yield 1
            yield 2
        except ValueError:
            print("捕获到异常")
    
    g = gen()
    next(g)
    g.throw(ValueError)  # 触发异常捕获

输出结果如下

image

四、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))

 

 

 

posted @ 2026-07-15 17:59  chenlight  阅读(35)  评论(0)    收藏  举报