栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

Python协程前奏-深度剖析yield、yield from

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Python协程前奏-深度剖析yield、yield from

文章目录
  • 1 yield
  • 2 yield from
  • 3 通过yield实现简单的生产者消费者

1 yield

函数体中出现yield的关键字,这个函数调用就是一个生成器

def gen():
    print("generator start")
    x = yield 1        
    print("received x =",x)
    
print(type(gen()))  # 

一个生成器对象有多种状态,可以通过客户端代码与生成器对象进行交互

客户端代码与生成器对象交互的三个主要方法:

  • send()
    生成器状态在GEN_SUSPENDED时,通过send方法向生成器传递值,生成器必须预激活,next(gen)或者gen.send(None)都可以用于预激活,如果不激活直接传递非None值,解释器会直接报错。
  • throw()
    向生成器抛出一个异常,如果生产期内部不处理这个异常,异常会上浮到客户端代码,如果客户端代码不捕获这个异常,那么会直接报错
  • close()
    给生成器一个停止停止。

下面的示例大家可以跑一下,如果能够理解这个过程,那么应该对生成器和yield有些理解

import inspect
from functools import wraps


def prime_generator(func):
    """
    预激活生成器的装饰器
    :return:
    """

    @wraps(func)
    def wrapper(*args, **kwargs):
        gen = func(*args, **kwargs)
        next(gen)
        return gen

    return wrapper


@prime_generator
def average():
    """
    生成器函数
    :return:
    """
    count = 0
    total = 0
    avg = 0
    while True:
        try:
            x = yield count, total, avg
        except ValueError:
            print("输入的值不符合要求")
            continue
        if x == None:
            break
        count += 1
        total += x
        avg = total / count


def client():
    """
    客户端代码
    :return:
    """
    avg = average()  # 创建生成器对象
    print(inspect.getgeneratorstate(avg))  # GEN_CREATED
    # s1 = next(avg) # 预激活生成器,生成器能运行,运行到第一个yield进入等待状态 , avg.send(None) 等同于 next(avg)
    # print(s1)
    while True:
        x = input("请输入值:")
        if x == "q":
            break
        try:
            x = float(x)
            res = avg.send(x)
            print(f"count:{res[0]},total:{res[1]},avg:{res[2]}")
        except ValueError:
            avg.throw(ValueError)
        print(inspect.getgeneratorstate(avg))  # GEN_SUSPENDED

    avg.close()
    print(inspect.getgeneratorstate(avg))  # GEN_CLOSED


if __name__ == '__main__':
    client()

2 yield from

引入yield from

def gen():
    for i in "AB":
        yield i
    for i in range(1,3):
        yield i

g = gen()

for i in g:
    print(i)
  
"""
A
B
1
2
"""

下面代码的输出结果与上面相同

def gen():
    yield from subgen()
    yield from subgen2()


def subgen():
    for i in "AB":
        yield i


def subgen2():
    for i in range(1, 3):
        yield i


g = gen()

for i in g:
    print(i)
"""
A
B
1
2
"""

首先,yield和yield from关键字必须写在函数体内。
含有yield from关键字的生成器称为委派生成器,yield from会把执行权交给其后面的生成器参数,当子生成器抛出StopIteration,执行权回到委派生成器内部。

from collections import namedtuple

Result = namedtuple('Result', ['count', 'average'])


def averager():
    total = 0.0
    count = 0
    average = 0
    while True:
        temp = yield
        if temp is None:
            break
        total += temp
        count += 1
        average = total / count
    return Result(count, average)


def grouper(results, key):
    while True:
        results[key] = yield from averager()


def report(results):
    for key, result in sorted(results.items()):
        group, unit = key.split(';')
        print('{:2} {:5} averaging {:.2f}{}'.format(result.count, group, result.average, unit))


def main(data):
    results = {}
    group = None
    for k, v in data.items():
        group = grouper(results, k)
        next(group)
        for v_ in v:
            group.send(v_)
        group.send(None)
    report(results)
    if group:  # 停止委派器生成器
         group.close()


data = {
    'girls;kg': [40, 41, 42, 43, 44, 54],
    'girls;m': [1.5, 1.6, 1.8, 1.5, 1.45, 1.6],
    'boys;kg': [50, 51, 62, 53, 54, 54],
    'boys;m': [1.6, 1.8, 1.8, 1.7, 1.55, 1.6],
}

if __name__ == '__main__':
    main(data)

通过一张图,来剖析一下这段代码的执行流程:


我的文字表达能力可能有限,解释的可能不清晰,如果读者有条件,可以把代码Copy到Pycharm中,打上断点,debug一下,看一下代码的执行流程,这样能够帮助大家更好的理解。

3 通过yield实现简单的生产者消费者
def consumer():
    while True:
        x = yield
        print(f"消费者消费了{x}")


def productor(c):
    next(c)
    while True:
        x = random.randint(0, 100)
        print(f"生产者生产了{x}")
        c.send(x)
        time.sleep(1)


if __name__ == '__main__':
    c = consumer()
    productor(c)

yield和yield 是python异步程序的基础,所以这是很重要的概念,这些概念相比同步还是比较抽象,所以不是很好理解,但是很重要。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/445302.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号