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. 惰性计算,适合大数据,内存友好。
相关推荐
前端snow38 分钟前
ai agent -- prompt汇总
前端
ltl1 小时前
SQLite Rollback Journal:写前拷贝与提交点
数据库
ltl1 小时前
TiFlash Learner:Raft 日志如何进列存
数据库
ltl1 小时前
BM25 与 Lucene Similarity:公式如何落到 postings
数据库
ltl1 小时前
RocksDB Block Cache 与 Bloom Filter:读放大怎么裁
数据库
zx_741484812 小时前
【Python 入门】面向对象基础:类、对象、成员变量与构造方法
开发语言·python
招财小梗2 小时前
沈阳商贸公司AI企业服务优势几何?
大数据·人工智能·python
muddjsv2 小时前
前端性能优化实战:从加载到渲染的全面提速指南
前端·性能优化
deli0070073 小时前
JSON 树形查看器:粘贴即解析,折叠展开一目了然
前端·数据库·json·ai编程