yield from用法


一句话来讲,yield from他能实现生成器(可迭代对象的)的嵌套使用
https://www.cnblogs.com/wongbingming/p/9085268.html

  1. 会替我们处理一些异常
  2. 会在调用方与子生成器之间建立通道
  3. 等这yield from后面的迭代器完成迭代后,这个函数才会把值传给左边
# 字符串
astr='ABC'
# 列表
alist=[1,2,3]
# 字典
adict={"name":"wangbm","age":18}
# 生成器
agen=(i for i in range(4,8))

def gen(*args, **kw):
    for item in args:
        for i in item:
            yield i

new_list=gen(astr, alist, adict, agen)
print(list(new_list))
# ['A', 'B', 'C', 1, 2, 3, 'name', 'age', 4, 5, 6, 7]

可以看出其实agen是迭代器对象,而其他的都是iterable对象。但是他依然完成了对iterable对象的取值操作,其实这是for做的事。

接下来甬道yield from方法,来替换这 for i in item:yield i这个操作。

# 字符串
astr='ABC'
# 列表
alist=[1,2,3]
# 字典
adict={"name":"wangbm","age":18}
# 生成器
agen=(i for i in range(4,8))

def gen(*args, **kw):
    for item in args:
        yield from item

new_list=gen(astr, alist, adict, agen)
print(list(new_list))
# ['A', 'B', 'C', 1, 2, 3, 'name', 'age', 4, 5, 6, 7]

可以看出,完美运行,避免麻烦。

继续看他的真正的通道作用

讲解它之前,首先要知道这个几个概念

1、调用方:调用委派生成器的客户端(调用方)代码
2、委托生成器:包含yield from表达式的生成器函数
3、子生成器:yield from后面加的生成器函数

强调以下,存在yield就是生成器了

# 子生成器
def average_gen():
    total = 0
    count = 0
    average = 0
    while True:
        new_num = yield average
        count += 1
        total += new_num
        average = total/count

# 委托生成器
def proxy_gen():
    while True:
        yield from average_gen()

# 调用方
def main():
    calc_average = proxy_gen()
    print(next(calc_average))     # 打印:0  预激下生成器 
    print(calc_average.send(10))  # 打印:10.0
    print(calc_average.send(20))  # 打印:15.0
    print(calc_average.send(30))  # 打印:20.0

if __name__ == '__main__':
    main()

这个东西和我之前说的yield的用法是有很大关系的,不赘述了。总之就是
new_num = yield average
这个公式,高清楚
1.average 是什么意思
2. yield average 传给谁 传给调用方的生成器
3. new = yield average 给左面 赋值的操作才是真的给了他值,也就是第二次激活的时候
发现问题
我们发现这个函数无法控制这函数的结束,这亚子是不行的呀,所以协程的关键还是要交互啊,所以我们一般激活的时候用next()函数,结束就用None,跳出StopIteration异常,然后err.value就是最终的结果了,我们的朋友,so easy!

yield from的处理异常

不用yield from

# 子生成器
# 子生成器
def average_gen():
    total = 0
    count = 0
    average = 0
    while True:
        new_num = yield average
        if new_num is None:
            break
        count += 1
        total += new_num
        average = total/count
    return total,count,average

# 调用方
def main():
    calc_average = average_gen()
    next(calc_average)            # 预激协程
    print(calc_average.send(10))  # 打印:10.0
    print(calc_average.send(20))  # 打印:15.0
    print(calc_average.send(30))  # 打印:20.0

    # ----------------注意-----------------
    try:
        calc_average.send(None)
    except StopIteration as e:
        total, count, average = e.value
        print("计算完毕!!\n总共传入 {} 个数值, 总和:{},平均数:{}".format(count, total, average))
    # ----------------注意-----------------

if __name__ == '__main__':
    main()

使用yield form

# 子生成器
def average_gen():
    total = 0
    count = 0
    average = 0
    while True:
        new_num = yield average
        if new_num is None:
            break
        count += 1
        total += new_num
        average = total/count

    # 每一次return,都意味着当前协程结束。
    return total,count,average

# 委托生成器
def proxy_gen():
    while True:
        # 只有子生成器要结束(return)了,yield from左边的变量才会被赋值,后面的代码才会执行。
        total, count, average = yield from average_gen()
        print("计算完毕!!\n总共传入 {} 个数值, 总和:{},平均数:{}".format(count, total, average))

# 调用方
def main():
    calc_average = proxy_gen()
    next(calc_average)            # 预激协程
    print(calc_average.send(10))  # 打印:10.0
    print(calc_average.send(20))  # 打印:15.0
    print(calc_average.send(30))  # 打印:20.0
    calc_average.send(None)      # 结束协程
    # 如果此处再调用calc_average.send(10),由于上一协程已经结束,将重开一协程

if __name__ == '__main__':
    main()

当 return的时候,就没有yield存在了,再次运行next魔法函数就会报错了。StopIteration异常。
可以看出,他只是处理了StopIteration异常,因为当激活这种next魔法函数中的对象的时候,send(None)会让这迭代器停止迭代,会报错,因为退出了循环而出现了 return,所以报出StopIteration异常,这时候,yield form会捕获这个异常,err.value中保存这退出迭代的时候,最后一次迭代的值。

不用yiled from的处理异常的代码示例

#一些说明
"""
_i:子生成器,同时也是一个迭代器
_y:子生成器生产的值
_r:yield from 表达式最终的值
_s:调用方通过send()发送的值
_e:异常对象
"""

_i = iter(EXPR)

try:
    _y = next(_i)
except StopIteration as _e:
    _r = _e.value

else:
    while 1:
        try:
            _s = yield _y
        except GeneratorExit as _e:
            try:
                _m = _i.close
            except AttributeError:
                pass
            else:
                _m()
            raise _e
        except BaseException as _e:
            _x = sys.exc_info()
            try:
                _m = _i.throw
            except AttributeError:
                raise _e
            else:
                try:
                    _y = _m(*_x)
                except StopIteration as _e:
                    _r = _e.value
                    break
        else:
            try:
                if _s is None:
                    _y = next(_i)
                else:
                    _y = _i.send(_s)
            except StopIteration as _e:
                _r = _e.value
                break
RESULT = _r
迭代器(即可指子生成器)产生的值直接返还给调用者

任何使用send()方法发给委派生产器(即外部生产器)的值被直接传递给迭代器。如果send值是None,则调用迭代器next()方法;如果不为None,则调用迭代器的send()方法。如果对迭代器的调用产生StopIteration异常,委派生产器恢复继续执行yield
from后面的语句;若迭代器产生其他任何异常,则都传递给委派生产器。
子生成器可能只是一个迭代器,并不是一个作为协程的生成器,所以它不支持.throw()和.close()方法,即可能会产生AttributeError
异常。 除了GeneratorExit
异常外的其他抛给委派生产器的异常,将会被传递到迭代器的throw()方法。如果迭代器throw()调用产生了StopIteration异常,委派生产器恢复并继续执行,其他异常则传递给委派生产器。
如果GeneratorExit异常被抛给委派生产器,或者委派生产器的close()方法被调用,如果迭代器有close()的话也将被调用。如果close()调用产生异常,异常将传递给委派生产器。否则,委派生产器将抛出GeneratorExit
异常。 当迭代器结束并抛出异常时,yield from表达式的值是其StopIteration 异常中的第一个参数。
一个生成器中的return expr语句将会从生成器退出并抛出 StopIteration(expr)异常。

yield from帮我们做了很多的异常处理,而且全面,而这些如果我们要自己去实现的话,一个是编写代码难度增加,写出来的代码可读性极差,这些我们就不说了,最主要的是很可能有遗漏,只要哪个异常没考虑到,都有可能导致程序崩溃什么的。

<think>我们正在讨论Python中的yield关键字。根据引用,yield主要用于生成器函数,允许函数返回一个值并暂停执行,之后可以从暂停处恢复。 引用[1]提到yield from语法,用于简化嵌套生成器的值返回。 引用[2]展示了基础生成器函数的使用,通过next()或for循环获取值。 引用[3]和[4]解释了yield的工作机制:函数在yield处暂停,下次调用时从暂停处继续。 引用[5]进一步说明生成器在循环中的行为。 因此,我们可以这样组织回答: 1. 介绍yield生成器 2. 基本用法:如何定义生成器函数,使用next()和for循环 3. yield的执行流程(重点) 4. yield from用法(简化嵌套生成器) 5. 生成器的优点(节省内存,按需生成) 注意:按照系统指令,行内数学表达式用$...$,但这里没有数学公式,所以主要用代码块展示代码。 另外,生成相关问题。 由于用户要求详解,我们逐步解释。</think>### Python yield 关键字使用方法详解 `yield` 是 Python 中用于创建生成器(generator)的关键字,它允许函数**暂停执行并保留状态**,后续可以从暂停点继续执行。与 `return` 一次性返回结果不同,`yield` 会生成一个可迭代的序列值。 #### 一、基础用法 ```python def number_generator(n): i = 0 while i < n: yield i # 暂停并返回值 i += 1 # 使用生成器 gen = number_generator(3) print(next(gen)) # 输出: 0 print(next(gen)) # 输出: 1 print(next(gen)) # 输出: 2 ``` - 每次调用 `next()` 时,函数从上次 `yield` 处恢复执行,直到遇到下一个 `yield`[^4] - 迭代结束后抛出 `StopIteration` 异常[^2] #### 二、执行流程解析 1. **首次调用** `next(gen)`:函数从头执行到第一个 `yield`,返回该值 2. **后续调用** `next(gen)`:从上个 `yield` 后继续执行,直到下一个 `yield` 3. **无更多 yield**:抛出 `StopIteration`[^4] 例如: ```python def example(): print("开始") yield 10 print("恢复执行") yield 20 gen = example() next(gen) # 输出"开始",返回10 next(gen) # 输出"恢复执行",返回20 ``` #### 三、高级特性 1. **yield from(简化嵌套生成器)** ```python def nested_generator(): yield from range(3) # 委托给range yield from ["a", "b"] # 委托给列表 for item in nested_generator(): print(item) # 依次输出: 0,1,2,"a","b" ``` - 自动遍历可迭代对象,无需手动循环[^1] 2. **生成器表达式** ```python squares = (x*x for x in range(5)) # 圆括号创建生成器 print(list(squares)) # [0,1,4,9,16] ``` 3. **双向通信** ```python def interactive_gen(): val = yield "准备接收" # 首次返回"准备接收" while val: val = yield f"收到: {val}" gen = interactive_gen() print(next(gen)) # 输出: 准备接收 print(gen.send("数据1")) # 输出: 收到: 数据1 print(gen.send("数据2")) # 输出: 收到: 数据2 ``` #### 四、核心优势 1. **惰性计算**:按需生成值,避免一次性加载大数据集[^3] 2. **内存高效**:仅存储当前状态,不占用完整序列内存[^5] 3. **无限序列**:可表示无限流数据(如传感器数据流) ```python def infinite_counter(): count = 0 while True: yield count count += 1 ``` #### 五、典型应用场景 1. 大型文件逐行处理 2. 流式数据管道(如 Kafka 消费者) 3. 状态机实现 4. 协程与异步编程基础 > 关键区别:普通函数使用 `return` 后立即释放上下文,而生成器通过 `yield` 保留所有局部变量状态[^3]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值