pythonyield和yieldfrom用法总结_python yield from用法

姓名:曾国强

学号:19021210984

转载自https://blog.csdn.net/qq_27825451/article/details/85244237

【嵌牛导读】

【嵌牛正文】

一、yield from 的简单实现

我们了解到,yield是每次“惰性返回”一个值,其实从名字中就能看出,yield from 是yield的升级改进版本,如果将yield理解成“返回”,那么yield from就是“从什么(生成器)里面返回”,这就构成了yield from的一般语法,即

yield from generator

这样的形式。我们通过一个简单例子来看:

def generator2():

yield 'a'

yield 'b'

yield 'c'

yield from [11,22,33,44]

yield from (12,23,34)

yield from range(3)

for i in generator2():

print(i,end=' , ')

'''运行的结果为:

a , b , c  , 11 , 22 , 33 , 44 , 12 , 23 , 34 , 0 , 1 , 2 ,

'''

总结:

从上面的代码可以看书,yield from 后面可以跟的可以是“ 生成器 、元组、 列表、range()函数产生的序列等可迭代对象”

简单地说,yield from  generator 。实际上就是返回另外一个生成器。而yield只是返回一个元素。从这个层面来说,有下面的等价关系:yield from iterable本质上等于 for item in iterable: yield item 。

二、yield from的高级应用

1、针对yield无法获取生成器return的返回值

我们都知道,在使用yield生成器的时候,如果使用for语句去迭代生成器,则不会显式的出发StopIteration异常,而是自动捕获StopIteration异常,所以如果遇到return,只是会终止迭代,而不会触发异常,故而也就没办法获取return的值。如下:

def my_generator():

for i in range(5):

if i==2:

return '我被迫中断了'

else:

yield i

def main(generator):

try:

for i in generator:

print(i)

except StopIteration as exc:

print(exc.value)

g=my_generator()

main(g)

'''运行结果为:0 1'''

从上面的例子可以看出,for迭代语句不会显式触发异常,故而无法获取到return的值,迭代到2的时候遇到return语句,隐式的触发了StopIteration异常,就终止迭代了,但是在程序中不会显示出来。

def my_generator():

for i in range(5):

if i==2:

return '我被迫中断了'

else:

yield i

def wrap_my_generator(generator):

result=yield from generator

print(result)

def main(generator):

for j in generator:

print(j)

g=my_generator()

wrap_g=wrap_my_generator(g)

main(wrap_g)

'''运行结果为:0 1 我被迫中断了'''

从上面的比较可以看出,yield from具有以下几个特点:

(1)上面的my_generator是原始的生成器,main是调用方,使用yield的时候,只涉及到这两个函数,即“调用方”与“生成器(协程函数)”是直接进行交互的,不涉及其他方法,即“调用方——>生成器函数(协程函数)”;

(2)在使用yield from的时候,多了一个对原始my_generator的包装函数,然后调用方是通过这个包装函数(后面会讲到它专有的名词)来与生成器进行交互的,即“调用方——>生成器包装函数——>生成器函数(协程函数)”;

(3)yield from iteration结构会在内部自动捕获 iteration生成器的StopIteration 异常。这种处理方式与 for 循环处理 StopIteration 异常的方式一样。而且对 yield from 结构来说,解释器不仅会捕获 StopIteration 异常,还会把return返回的值或者是StopIteration的value 属性的值变成 yield from 表达式的值,即上面的result。

2、yield from所实现的数据传输通道

前面总结的几个特点里面已经介绍了yield和yield from的数据交互方式,yield涉及到“调用方与生成器两者”的交互,生成器通过next()的调用将值返回给调用者,而调用者通过send()方法向生成器发送数据;

但是yield还有一个第三者函数,下面将先从相关的概念说起。

在PEP 380 使用了一些yield from使用的专门术语:

委派生成器:包含 yield from 表达式的生成器函数;即上面的wrap_my_generator生成器函数

子生成器:从 yield from 表达式中 部分获取的生成器;即上面的my_generator生成器函数

调用方:调用委派生成器的客户端代码;即上面的main生成器函数

下图是这三者之间的交互关系(摘自博客园):

委派生成器在 yield from 表达式处暂停时,调用方可以直接把数据发给子生成器,子生成器再把产出的值发给调用方。子生成器返回之后,解释器会抛出StopIteration 异常,并把返回值附加到异常对象上,此时委派生成器会恢复。

总结:

(1)yield from主要设计用来向子生成器委派操作任务,但yield from可以向任意的可迭代对象委派操作;

(2)委派生成器(group)相当于管道,所以可以把任意数量的委派生成器连接在一起---一个委派生成器使用yield from 调用一个子生成器,而那个子生成器本身也是委派生成器,使用yield from调用另一个生成器。

(3)针对yield存在的第二个缺点

首先看一下他要表述的意思是什么?它的局限性在于只能向它的直接调用者每次yield一个值。这意味着那些包含yield的代码不能像其他代码那样被分离出来放到一个单独的函数中。这也正是yield from要解决的。具体参见上文:

https://blog.csdn.net/qq_27825451/article/details/85234610

这句话确实难以理解,但是他要表达的意思实际上是:因为生成器从定义上来看,就像是一个普通的函数,那么既然作为普通函数,就应该可以反反复复调用都没问题的,但是生成器却并不行。那为什么yield from可以解决这样的问题呢,主要是因为yield from后面可以跟任意一个生成器,即yield from可以将任意的任务为派给任意生成器函数,从而避免了子生成器直接向调用者返回单个值的情况。

三、yield from的用法示例

其实yield from最重要的作用就是提供了一个“数据传输的管道”,下面通过一个简单的例子加以说明为什么是管道:

def average():

total = 0

count = 0

avg = None

while True:

num = yield avg

total += num

count += 1

avg = total/count

def wrap_average(generator):

yield from generator

def main(wrap):

print(next(wrap))

print(wrap.send(10))

print(wrap.send(20))

print(wrap.send(30))

print(wrap.send(40))

g = average()

wrap=wrap_average(g)

main(wrap)

'''运行结果为:None 10.0 15.0 20.0 25.0'''

从上面我们可以发现,调用方发送的数据是发给wrap_average的,怎么依然到了生成器函数average里面呢?这就是“数据传输管道的作用”。即主函数调用方main把各个value传给grouper ,而这个传入的值最终到达averager函数中; grouper并不知道传入的是什么值,因为从上面的代码看出,wrap_average里面完全没有处理这个值的任何代码!

已标记关键词 清除标记
表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
相关推荐
©️2020 CSDN 皮肤主题: 深蓝海洋 设计师:CSDN官方博客 返回首页