Python 高级编程和异步IO并发编程 --12_7 生成器进阶- yield from

# Python 3.3新加了yield from语法
from itertools import chain # chain可以将迭代的类型给连接起来,直接做一个for循环
my_list = [1,2,3]
my_dict = {
    "Baidu":"http://www.baidu.com",
    "Sohu":"http://www.sohu.com",
}

for value in chain(my_list,my_dict,range(5,10)):
    print(value)
1
2
3
Baidu
Sohu
5
6
7
8
9
# Python 3.3新加了yield from语法
from itertools import chain # chain可以将迭代的类型给连接起来,直接做一个for循环
my_list = [1,2,3]

my_dict = {
    "Baidu":"http://www.baidu.com",
    "Sohu":"http://www.sohu.com",
}

def my_chain(*args,**kwargs):  # 当传入的参数不确定是,用*args
    for my_iterable in args:   # args是tuple,属于可迭代对象
        for value in my_iterable:
            yield value

for value in my_chain(my_list,my_dict,range(5,10)):
    print(value)

用my_chain函数,模拟实现chain的功能,发现有相同的效果。

# 用yield from实现
my_list = [1,2,3]

my_dict = {
    "Baidu":"http://www.baidu.com",
    "Sohu":"http://www.sohu.com",
}
# yield from 后面是iterable对象
def my_chain(*args,**kwargs):  # 当传入的参数不确定是,用*args
    for my_iterable in args:   # args是tuple,属于可迭代对象
        yield from my_iterable

for value in my_chain(my_list,my_dict,range(5,10)):
    print(value)

yield与yield from的区别

def g1(iterable):
    yield iterable  # yield,iterable是什么样子,输出就是什么样子。

def g2(iterable):
    yield from iterable  # yield from会把iterable的每个值都给迭代出来

for value in g1(range(10)):
    print(value)    # range(0, 10)

for value in g2(range(10)):
    print(value)    # 0,1,2,3,4,5,6,7,8,9
range(0, 10)
0
1
2
3
4
5
6
7
8
9
# 用yield from实现
my_list = [1,2,3]

my_dict = {
    "Baidu":"http://www.baidu.com",
    "Sohu":"http://www.sohu.com",
}

def my_chain(*args,**kwargs):
    for my_iterable in args:
        yield from my_iterable

for value in my_chain(my_list,my_dict,range(5,10)):
    print(value)

def g1(gen):
    yield from gen

def main():
    g = g1()
    g.send(None)

#1. main为调用方。g1(委托生成器),gen(子生成器)
#1 yield from会在调用方main与子生成器gen之间建立一个双向通道。即yield执行完,将值直接交给main;甚至main生成的异常可以直接传递给gen
final_result = {}

def sales_sum(pro_name):
    total = 0
    nums = []
    while True:
        x = yield
        print(pro_name+"销售: ",x)
        if not x:
            break
        total += x
        nums.append(x)
    return total,nums

def middle(key):
    while True:
        final_result[key] = yield from sales_sum(key)
        print(key+"销售统计完成!!.")

def main():
    data_sets = {
        "Tom-1牌面膜":[1200,1500,3000], # 销量
        "Tom-2牌手机": [200, 55, 98,108],
        "Tom-3牌大衣": [280, 560, 778,70]
    }
    for key,data_set in data_sets.items():
        print("start key:",key)
        m = middle(key)
        m.send(None) # 预激middle协程
        for vale in data_set:
            m.send(vale)  # 给协程传递每一组的值
        m.send(None)
    print("final result:",final_result)

if __name__ == "__main__":
    main()
start key: Tom-1牌面膜
Tom-1牌面膜销售:  1200
Tom-1牌面膜销售:  1500
Tom-1牌面膜销售:  3000
Tom-1牌面膜销售:  None
Tom-1牌面膜销售统计完成!!.
start key: Tom-2牌手机
Tom-2牌手机销售:  200
Tom-2牌手机销售:  55
Tom-2牌手机销售:  98
Tom-2牌手机销售:  108
Tom-2牌手机销售:  None
Tom-2牌手机销售统计完成!!.
start key: Tom-3牌大衣
Tom-3牌大衣销售:  280
Tom-3牌大衣销售:  560
Tom-3牌大衣销售:  778
Tom-3牌大衣销售:  70
Tom-3牌大衣销售:  None
Tom-3牌大衣销售统计完成!!.
final result: {'Tom-1牌面膜': (5700, [1200, 1500, 3000]), 'Tom-2牌手机': (461, [200, 55, 98, 108]), 'Tom-3牌大衣': (1688, [280, 560, 778, 70])}
final_result = {}

def sales_sum(pro_name):
    total = 0
    nums = []
    while True:
        x = yield
        print(pro_name+"销售: ",x)
        if not x:
            break
        total += x
        nums.append(x)
    return total,nums

if __name__ == "__main__":
    my_gen = sales_sum("Tom牌手机")
    my_gen.send(None) # 预激活
    my_gen.send(1200)
    my_gen.send(1500)
    my_gen.send(3000)
    try:
        my_gen.send(None)
    except StopIteration as e:
        result = e.value
        print(result)
Tom牌手机销售:  1200
Tom牌手机销售:  1500
Tom牌手机销售:  3000
Tom牌手机销售:  None
(5700, [1200, 1500, 3000])

pep380

关于 yeild from

1. RESULT = yield from EXPR可以简化成下面这样

一些说明

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

"""
_i = iter(EXPR) # EXPR是一个可迭代对象,_i其实是子生成器;
try:
    _y = next(_i) # 预激子生成器,把产出的第一个值存在_y中;
except StopIteration as _e:
    _r = _e.value # 如果抛出了StopIteration异常,那么就将异常对象的value属性保存到_r,这是最简单的情况的返回值;
else: while 1: # 尝试执行这个循环,委托生成器会阻塞;
    _s = yield _y # 生产子生成器的值,等待调用方send()值,发送过来的值将保存在_s中;
    try:
        _y = _i.send(_s) # 转发_s,并且尝试向下执行;
    except StopIteration as _e:
        _r = _e.value # 如果子生成器抛出异常,那么就获取异常对象的value属性存到_r,退出循环,恢复委托生成器的运行;
        break
RESULT = _r # _r就是整个yield from表达式返回的值。
"""
    子生成器可能只是一个迭代器,并不是一个作为协程的生成器,所以它不支持.throw()和.close()方法;
    如果子生成器支持.throw()和.close()方法,但是在子生成器内部,这两个方法都会抛出异常;
    调用方让子生成器自己抛出异常

    当调用方使用next()或者.send(None)时,都要在子生成器上调用next()函数,当调用方使用.send()发送非 None 值时,才调用子生成器的.send()方法; 
"""
_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()发送的值都是直接传递给子生成器的;如果发送的是 None,会调用子生成器的next()方法,如果不是 None,会调用子生成器的.send()方法;
    子生成器退出的时候,最后的return EXPR,会触发一个StopIteration(EXPR)异常;
    yield from表达式的值,是子生成器终止时,传递给StopIteration异常的第一个参数;
    如果调用的时候出现StopIteration异常,委托生成器会恢复运行,同时其他的异常会向上 "冒泡";
    传入委托生成器的异常里,除了GeneratorExit之外,其他的所有异常全部传递给子生成器的.throw()方法;如果调用.throw()的时候出现了StopIteration异常,那么就恢复委托生成器的运行,其他的异常全部向上 "冒泡";
    如果在委托生成器上调用.close()或传入GeneratorExit异常,会调用子生成器的.close()方法,没有的话就不调用。如果在调用.close()的时候抛出了异常,那么就向上 "冒泡",否则的话委托生成器会抛出GeneratorExit异常。

"""
发布了380 篇原创文章 · 获赞 129 · 访问量 11万+

猜你喜欢

转载自blog.csdn.net/f2157120/article/details/105206801
今日推荐