yield

yield

yield 用来写生成器(generator)

函数里面一旦出现 yield,这个函数就不再是普通函数,调用它不会直接执行函数体,返回一个生成器对象(迭代器)。

return:直接返回结果,函数终止

yield:产出一个值,暂停当前函数,保存现场;下次迭代再从暂停位置继续往下跑

最简单示例

python 复制代码
def gen():
    yield 1
    yield 2
    yield 3

g = gen()   # 不会执行函数内部!拿到生成器对象
print(g)    # <generator object gen at ...>

print(next(g)) # 走到第一个yield,产出1,暂停 →输出1
print(next(g)) # 从暂停处继续,走到yield2,产出2 →输出2
print(next(g)) # 走到yield3,产出3 →输出3
print(next(g)) # 函数跑完,抛出 StopIteration

也可以 for 遍历:

python 复制代码
for v in gen():
    print(v)
# 1  2  3

yield 和 return 的核心对比

  • return val:返回值,函数直接结束,局部状态全部销毁
  • yield val:产出值,挂起函数,保存局部变量、执行位置;下次next恢复继续执行
python 复制代码
def f_ret():
    return 1
    return 2 # 永远跑不到

def f_yield():
    yield 1
    yield 2 # 可以跑到

带状态:yield会保存函数局部变量

python 复制代码
def count():
    n = 0
    while n < 3:
        n += 1
        yield n

g = count()
print(next(g)) #1
print(next(g)) #2
print(next(g)) #3

n 的值被保存,不会每次调用重置。普通函数调用一次就销毁局部变量,生成器会保留现场。

关键坑:调用生成器函数不会跑代码

python 复制代码
def gen():
    print("start")
    yield 1

gen()   # 啥都不打印!仅仅拿到生成器
g = gen()
next(g) # 这时才打印 start,产出1

结合之前学的 @contextmanager

python 复制代码
from contextlib import contextmanager

@contextmanager
def demo():
    print("进入,获取资源")
    yield "传给as的值"   # ← with的as拿到这个值
    print("退出,释放资源")

with demo() as v:
    print(v)

执行流程:

  1. 进入with,执行到 yield,产出值给v函数暂停
  2. 运行with缩进块里面业务代码
  3. 离开with块,恢复生成器,执行yield后面的代码(释放资源)

⚠️ 如果with内部抛异常,异常会在yield位置抛出来。所以 @contextmanager 内部一般要包 try‑finally

python 复制代码
@contextmanager
def demo():
    print("获取")
    try:
        yield "xxx"
    finally:
        print("释放,一定执行")

如果不写try‑finally,with内部抛异常,yield后面的清理代码不会执行!


yield from(委托生成器)

把另一个生成器的所有值直接产出,不用手写循环:

python 复制代码
def sub():
    yield 10
    yield 20

def main():
    yield from sub()
    yield 30

for x in main():
    print(x) #10,20,30

send():向生成器发送值(进阶)

yield 可以作为表达式接收外部传入的值:

python 复制代码
def gen():
    res = yield "a"
    print(f"收到:{res}")
    yield "b"

g = gen()
print(next(g))      #产出 a
print(g.send(666))  #把666传给 yield表达式,res=666,继续执行产出b

生成器优点:惰性求值

不是一次性把全部数据加载到内存,用一个算一个。处理超大文件、无限数据流非常好用,不占内存。

python 复制代码
# 不会一次性生成100万元素,迭代一个算一个
def big_data(n):
    for i in range(n):
        yield i

for num in big_data(1_000_000):
    if num>10:
        break

极简总结

  1. 函数写了yield →变成生成器函数 ;调用返回生成器对象,不执行函数体
  2. next() / for迭代,驱动函数运行,遇到yield就产出数据、暂停,保存现场。
  3. yield暂停;return直接结束函数。
  4. @contextmanager就是利用生成器:yield之前=进入上下文;yield之后=退出清理。清理逻辑必须放finally里,否则异常会跳过清理
  5. 惰性计算,适合大数据,内存友好。
相关推荐
看谷秀3 小时前
arkts-10 实战
前端·arkts
范小兵3 小时前
# DevEco CLI实战:鸿蒙App「至客」从0开发到正式上架
前端·harmonyos
半生过往3 小时前
前端工程师学习智能体开发(一)
前端·学习·状态模式
白远山3 小时前
健身场馆无人自动化解决方案:从架构到落地实践
java·开发语言·数据库·数据挖掘·需求分析
白雾茫茫丶3 小时前
Vibecoding 一个主题切换动画库:13 种揭幕方式
前端·vue.js·react.js
虎虎(_ _)。゜zzZ3 小时前
SQLAlchemy入门教程
数据库·后端·python·sql·ai·sqlalchemy
计算机魔术师3 小时前
谷歌宣布 TPU 互联架构支持 100 万芯片规模,电力供应成 AI 基建核心瓶颈
前端
雪芽蓝域zzs4 小时前
第四十九节:TagsView 右键菜单(带三角箭头)给每个 tag 增加**鼠标右键菜单**(右键标签弹出:关闭、关闭其他、关闭全部)
前端·javascript·vue.js
এ慕ོ冬℘゜4 小时前
从零开发移动端医院挂号页面:适配、交互与前端实现详解
前端·javascript
BUG研究员_4 小时前
LangGraph持久化之失败后恢复运行
python·agent