美文网首页
yield from x 委派生成器

yield from x 委派生成器

作者: 阿登20 | 来源:发表于2021-04-02 19:22 被阅读0次

    yield去实现列表求和

    res=g.send(value) 传值的步骤: value先传给yield, 然后yield传给左边等号的变量x.然后运行yield下面的代码直到下次到yield处进行挂起。然后把yield返回值 返回给res

    a = [1,3,5,6,9]
    # 计算列表的和
    def add(x:list):
        summ = 0
        index = 0
        while index < len(x):
            summ += x[index]
            index += 1
        print(summ)
    add(a)
    
    #如果用yied去实现呢
    
    def add1():
        res = 0
        while True:
            x = yield
            if x is None: #seed传值为None用来结束循环
                break
            res += x
            print(res)
        return res
    
    g = add1()
    next(g)
    
    for i in a:
        g.send(i)
    # 获取生成器的值需要捕获 StopIteration异常 e.value作为返回值。
    # for循环生成器 会自动捕获StopIteration,不会有生成器的返回值。
    try:
        next(g)
    except StopIteration as e:
        print(e.value)
    
    """
    yield from x  x可以是range函数 可迭代对象如列表 字符串 元组 字典, 函数等等
    yield from x 类似看成  for i in x: yield i  i是子生成器StopIteration异常的 return的返回值。
    
    """
    
    
    

    yield from x 委派生成器

    yield from x x可以是range函数 可迭代对象如列表 字符串 元组 字典, 函数等等 yield from x 类似看成 for i in x: yield i i是子生成器StopIteration异常的 return的返回值。

    def fo():# fo看作为委派生成器
        for i in range(5):
            yield i
        for m in ("a", "b", "c"):
            yield m
        for x in ("a", "b", "c"): yield x
    
    # 生成器的调用
    f = fo()
    next(f) # 生成器初始化 这步必须有
    print(list(f)) # [1, 2, 3, 4, 'a', 'b', 'c', 'name', 'age', 'hobby']
    
    # 其实fo函数可以简写为
    def fo():
        yield from range(5)
        yield from ("a", "b", "c")
        yield from ("a", "b", "c")
    
    f = fo()
    next(f) # 生成器初始化 这步必须有
    print(list(f)) # [1, 2, 3, 4, 'a', 'b', 'c', 'name', 'age', 'hobby']
    

    yield from 函数 生成器递归调用

    # yield的一个递归案例
    # 去除对象5所有的元素到一个列表
    a = [1,3,4,["啊登","向佳","咸维维"],[[15,19,["香蕉",["西瓜","芒果"]]],8,0]]
    
    
    def get_a(a,b):
        b = b
        for i in a:
            if isinstance(i,list) and (not isinstance(i,(str, bytes))) :  #b"xxx" bytes数据
                get_a(i,b)
            else:
                b.append(i)
        return b
    
    print(get_a(a, []))
    
    # 生成器来实现
    a = [1,3,4,["啊登","向佳","咸维维"],[[15,19,["香蕉",["西瓜","芒果"]]],8,0],(456,797)]
    from collections.abc import Iterable
    
    def get_a(a):
    
        for i in a:
            if isinstance(i,Iterable) and (not isinstance(i,(str, bytes))) :  #b"xxx" bytes数据
                yield from get_a(i)  #生成器函数调用自己本身用yield from 函数
            else:
                yield i
    print(tuple(get_a(a)))
    #(1, 3, 4, ['啊登', '向佳', '咸维维'], [[15, 19, ['香蕉', ['西瓜', '芒果']]], 8, 0], (456, 797))
    
    """
    yield from x x是一个Iterabel 可迭代对象。 range() list 元组 字典 字符串 都适用。
    这里 yield from x 前面没有用一个参数 去接收 子生成器的返回值。
    y = yield from x 子生成器StopIteration异常抛出,return的返回值 y来接收。
    yield from 另外一个作用就是可以给子生成器 send值,当send None子生成器就跳出循环。
    """
    
    

    yield from 委派生成器给子生成器传值过程

    def foo(list_total:list):
        # 委派生成器
    
        while True:
            # x 接收fo生成器的StopIteration,即return返回值。然后接着这行委派生成器yield from下面的代码直到下次yield from处
            x = yield from fo()
            list_total.append(x)
    
    def fo():
        # 子生成器
        total = 0
        while True:
            y = yield
            if y is None:
                return total
            total += y
    
    
    list_total = []
    g = foo(list_total )
    next(g) # 初始化,准备给子生成器fo传值.这是后代码会运行到fo函数的yield处
    # 循环传值
    for i in range(5,9):
        g.send(i)
    g.send(None)  # foo委派生成器 x会接收 fo子生成器return 的返回值total会运行yield from下面的代码 再次进去fo的yield出等待传值
    
    for i in range(20,25):
        g.send(i)
    g.send(None)  # foo委派生成器 x会接收 fo子生成器return 的返回值total会运行yield from下面的代码 再次进去fo的yield出等待传值
    
    print(list_total) # [26, 110]
    

    子生成器的基本框架

        while True:
            x = yield  # 委派生成器给ta传值
            if x is None:  # 传值为None 终止子生成器
               break
            .......
        ......
    

    委派生成器框架

    def foo():
    while True:
      res = yield from fo()
      ................
    
    

    使用yeild from写一个异步爬虫

    import requests
    from collections import namedtuple  ①
    
    Response = namedtuple("rs", 'url status') ②
    
    
    # 子生产器
    def fecth(): ③
        res=[]
        while 1:
            url = yield ④
            if url is None: ⑤
                break
            req = requests.get(url)
            res.append(Response(url=url, status=req.status_code))
        return res
    
    #委派生成器
    def url_list(l, key):
        while 1: ⑥
            l[key] = yield from fecth() ⑦
    
    #调用方
    def main():
        l = {}
        u = ["http://www.baidu.com", "http://www.cnblogs.com"]
        for index, url in enumerate(u):
            if index == 0:
                ul = url_list(l, index)
                next(ul) ⑧
            ul.send(url)⑨
        ul.send(None)⑩
        return l
    
    
    if __name__ == '__main__':
        res = main()
        print(res)
    

    接下来对上面的标准进行解释:
    ① 引入一个具名元组,可以后面实现一个简单的类。
    ② 对请求参数做一个格式化处理,后面通过获取属性即可。
    ③一个协程,通过requests模块可以发起网络请求。
    ④main函数的发送的值绑定到这里的url上
    ⑤ url为None即没有url的时候结束循环的。
    ⑥这个循环每次都会新建一个fetch 实例,每个实例都是作为协程使用的生成器对象。
    ⑦ url_list发送的每个值都会经由yield from 处理,然后传给fetch 实例。url_list会在yield from表达式处暂停,等待fetch实例处理客户端发来的值。fetch实例运行完毕后,返回的值绑定到l[key] 上。while 循环会不断创建fetch实例,处理更多的值。
    ⑧激活url_list生成器
    ⑨把各个url以及其序列号index,传给url_list传入的值最终到达fetch函数中,url_list并不知道传入的是什么,同时url_list实例在yield from处暂停。直到fetch的一个实例处理完才进行赋值。
    ⑩关键的一步,ul把None传入url_list,传入的值最终到达fetch函数中,导致当前实例终止。然后继续创建下一个实例。如果没有ul.send(None),那么fetch子生成器永远不会终止,因为ul.send()发送的值实际是在fetch实例中进行,委派生成器也永远不会在此激活,也就不会为l[key]赋值

    相关文章

      网友评论

          本文标题:yield from x 委派生成器

          本文链接:https://www.haomeiwen.com/subject/dfmqkltx.html