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. 惰性计算,适合大数据,内存友好。
相关推荐
“AI国潮设计-小江”3 小时前
《Python+SDXL实战:用ControlNet精准控制“英歌舞翻糖吐司”构图,附批量生成脚本》
开发语言·人工智能·python·prompt·aigc
wenwen_chao3 小时前
Python学习与开发资源合集:实用技巧与工具推荐
python
陈随易3 小时前
bm2,Node.js 与 Bun 项目部署新选择
前端·后端·程序员
for_ever_love__4 小时前
函数——参数、lambda、*args与作用域
python·函数·大模型开发·编程基础
东莞市云毅网络有限公司4 小时前
企业资料时效性判定:Last-Modified、正文日期与版本字段三路交叉
python·sqlite·自动化运维·geo·数据监测
Query*4 小时前
深入浅出LangGraph【一】_基础篇
python·ai·langchain
福兮说5 小时前
canvas 旋转图片的六个坑:四角被裁、Math.ceil 多出 1px 黑边、JPG 角发黑、翻转方向反了
前端·javascript·图像处理·canvas
XiaoMaqqqq5 小时前
市面上正规的IP驱动产业新场景新工具哪家强
运维·python·网络协议·tcp/ip
许彰午5 小时前
01-Oracle数据库简介与版本选择
数据库·oracle
菜鸟~noob2335 小时前
【电子战】第三季预告—波武器与电子系统强力毁伤
开发语言·数据库·matlab·电子战