一句话来讲,yield from他能实现生成器(可迭代对象的)的嵌套使用
https://www.cnblogs.com/wongbingming/p/9085268.html
- 会替我们处理一些异常
- 会在调用方与子生成器之间建立通道
- 等这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帮我们做了很多的异常处理,而且全面,而这些如果我们要自己去实现的话,一个是编写代码难度增加,写出来的代码可读性极差,这些我们就不说了,最主要的是很可能有遗漏,只要哪个异常没考虑到,都有可能导致程序崩溃什么的。