资讯专栏INFORMATION COLUMN

流畅的python读书笔记-第十六章-携(协)程

wanglu1209 / 3151人阅读

摘要:当前状态可以使用函数确定,该函数会返回下述字符串中的一个。解释器正在执行。打印消息,然后协程终止,导致生成器对象抛出异常。实例运行完毕后,返回的值绑定到上。

协程

协程可以身处四个状态中的一个。

当前状态可以使用inspect.getgeneratorstate(...) 函数确定,该函数会返回下述字符串中的一个。

"GEN_CREATED"
  等待开始执行。

"GEN_RUNNING"
  解释器正在执行。

"GEN_SUSPENDED"
  在 yield 表达式处暂停。

"GEN_CLOSED"
  执行结束。

def simple_coro2(a):
    print("-> Started: a =", a)


    b = yield a  #等着赋值b 把a甩出去
    print("-> Received: b =", b)
    c = yield a + b
    print("-> Received: c =", c)


my_coro_2 = simple_coro2(14)

from inspect import getgeneratorstate

print(getgeneratorstate(my_coro_2))

print(next(my_coro_2))
getgeneratorstate(my_coro_2)


print(my_coro_2.send(28))

# 没有yield 出来 所以没有返回值
print(my_coro_2.send(99))

getgeneratorstate(my_coro_2)

getgeneratorstate 函数指明,处于 GEN_SUSPENDED 状态(即协程在 yield 表达式处暂停)。

❺ 把数字 99 发给暂停的协程;计算 yield 表达式,得到 99,然后把那个数绑定给 c。
打印 -> Received: c = 99 消息,然后协程终止,导致生成器对象抛出
StopIteration 异常。

另一个案例
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total / count


coro_avg = averager()

print(next(coro_avg))

print(coro_avg.send(10))

print(coro_avg.send(15))

print(coro_avg.send(20))

调用 next 函数,预激协程。
➊ 这个无限循环表明,只要调用方不断把值发给这个协程,它就会一直接收值,然后生
成结果。仅当调用方在协程上调用 .close() 方法,或者没有对协程的引用而被垃圾回收
程序回收时,这个协程才会终止。
➋ 这里的 yield 表达式用于暂停执行协程,把结果发给调用方;还用于接收调用方后面
发给协程的值,恢复无限循环。

终止协程和异常处理

发送某个哨符值,让协程退出。
内置的 None 和Ellipsis 等常量经常用作哨符值。

Ellipsis 的优点是,数据流中不太常有这个值。

throw

generator.throw(exc_type[, exc_value[, traceback]])

致使生成器在暂停的 yield 表达式处抛出指定的异常。

如果生成器处理了抛出的异常,代码会向前执行到下一个 yield 表达式,而产出的值会成为调用 generator.throw方法得到的返回值。

generator.close()

致使生成器在暂停的 yield 表达式处抛出 GeneratorExit 异常。

如果生成器没有处理这个异常,或者抛出了 StopIteration 异常(通常是指运行到结尾),调用方不会报错。

如果收到 GeneratorExit 异常,生成器一定不能产出值,否则解释器会抛出
RuntimeError 异常。

两种停止方式

exc_coro.throw(ZeroDivisionError)
exc_coro.close()

让协程返回值
from collections import namedtuple

Result = namedtuple("Result", "count average")


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


coro_avg = averager()
next(coro_avg)

coro_avg.send(30)
coro_avg.send(6.5)

try:
    coro_avg.send(None)
except StopIteration as exc:
    result = exc.value
    print(result)

捕获 StopIteration 异常,获取 averager 返回的值

yield from 结构会在内部自动捕获 StopIteration 异常。
这种处理方式与 for 循环处理 StopIteration 异常的方式一样:循环机制使用用户易于理解的方式处理异常。
对 yield from 结构来说,解释器不仅会捕获 StopIteration 异常,还会把value 属性的值变成 yield from 表达式的值。
使用yield from

yield from 结构唯一的作用是替代产出值的嵌套 for 循环, 这句话不对

yield from 的主要功能是打开双向通道,把最外层的调用方与最内层的子生成器连接起来,

这样二者可以直接发送和产出值,还可以直接传入异常,

而不用在位于中间的协程中添加大量处理异常的样板代码。

有了这个结构,协程可以通过以前不可能的方式委托职责。

案例

委派生成器
  包含 yield from 表达式的生成器函数。

子生成器
  从 yield from 表达式中 部分获取的生成器。这就是 PEP 380 的标题
(“Syntax for Delegating to a Subgenerator”)中所说的“子生成器”(subgenerator)。

调用方
  PEP 380 使用“调用方”这个术语指代调用委派生成器的客户端代码。在不同的语境
中,我会使用“客户端”代替“调用方”,以此与委派生成器(也是调用方,因为它调用了子
生成器)区分开。

不使用yield from

from collections import namedtuple

Result = namedtuple("Result", "count average")


# 子生成器
def averager():  # ➊
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield  # ➋
        if term is None:  # ➌
            break
        total += term
        count += 1
        average = total / count
    return Result(count, average)  # ➍



data = {
    "girls;kg":
        [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
    "girls;m":
        [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
    "boys;kg":
        [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
    "boys;m":
        [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}




def main(data):
    result = {}

    for key, values in data.items():
        coro_avg = averager()
        next(coro_avg)
        for value in values:
            coro_avg.send(value)

        try:
            coro_avg.send(None)
        except StopIteration as exc:
            result[key] = exc.value

    print(result)



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

这里的try: catch stop异常要一直存在

用yiled from 及委派生成器作用

from collections import namedtuple

Result = namedtuple("Result", "count average")


# 子生成器
def averager():  # ➊
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield  # ➋
        if term is None:  # ➌
            break
        total += term
        count += 1
        average = total / count
    return Result(count, average)  # ➍



data = {
    "girls;kg":
        [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
    "girls;m":
        [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
    "boys;kg":
        [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
    "boys;m":
        [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}


def grouper(results):
    while True:
        res_obj = yield from averager()
        results.append(res_obj)




def main(data):
    results = []
    for key, values in data.items():

        coro_avg = grouper(results)
        next(coro_avg)

        for value in values:
            coro_avg.send(value)

        # 这个None是停止返回 哨兵
        coro_avg.send(None)


    print(results)

    # report(result)


# 输出报告
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))


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

官方的案例

from collections import namedtuple

Result = namedtuple("Result", "count average")


# 子生成器
def averager():  # ➊
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield  # ➋
        if term is None:  # ➌
            break
        total += term
        count += 1
        average = total / count
    return Result(count, average)  # ➍


# 委派生成器
def grouper(results, key):  # ➎
    while True:  # ➏
        results[key] = yield from averager()  # ➐




# 客户端代码,即调用方
def main(data):  # ➑
    results = {}
    for key, values in data.items():
        group = grouper(results, key)  # ➒
        next(group)  # ➓
        for value in values:
            group.send(value)  # ⓫
        group.send(None)  # 重要! #⓬

    print(results)  # 如果要调试,去掉注释

    # report(results)


# 输出报告
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))


data = {
    "girls;kg":
        [40.9, 38.5, 44.3, 42.2, 45.2, 41.7, 44.5, 38.0, 40.6, 44.5],
    "girls;m":
        [1.6, 1.51, 1.4, 1.3, 1.41, 1.39, 1.33, 1.46, 1.45, 1.43],
    "boys;kg":
        [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3],
    "boys;m":
        [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46],
}

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

❸ 至关重要的终止条件。如果不这么做,使用 yield from 调用这个协程的生成器会永
远阻塞。
❹ 返回的 Result 会成为 grouper 函数中 yield from 表达式的值

❻ 这个循环每次迭代时会新建一个 averager 实例;每个实例都是作为协程使用的生成
器对象。
❼ grouper 发送的每个值都会经由 yield from 处理,通过管道传给 averager 实
例。grouper 会在 yield from 表达式处暂停,等待 averager 实例处理客户端发来的
值。averager 实例运行完毕后,返回的值绑定到 results[key] 上。while 循环会不断
创建 averager 实例,处理更多的值。

yield from 结构会在内部自动捕获 StopIteration 异常。这种处理方
式与 for 循环处理 StopIteration 异常的方式一样:循环机制使用用户易于理解的方式
处理异常。对 yield from 结构来说,解释器不仅会捕获 StopIteration 异常,还会把
value 属性的值变成 yield from 表达式的值。
总结

这里先不总结 带我把协程 程序加进去

如何使用协程在单个线程中管理并发活动。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/42066.html

相关文章

  • 流畅python读书笔记-第六章-使用一等函数实现设计模式

    摘要:在复杂的情况下,需要具体策略维护内部状态时,可能需要把策略和享元模式结合起来。函数比用户定义的类的实例轻量,而且无需使用享元模式,因为各个策略函数在编译模块时只会创建一次。 一等函数实现设计模式 经典的策略模式定义 定义一系列算法,把它们一一封装起来,并且使它们可以相互替换。本模式使得算法可以独立于使用它的客户而变化。 案例 假如一个网店制定了下述折扣规则。 有 1000 或以上积分...

    cnsworder 评论0 收藏0
  • python2:yield from 从入门到精通

    摘要:于此同时,会阻塞,等待终止。子生成器返回之后,解释器会抛出异常,并把返回值附加到异常对象上,只是委派生成器恢复。实例运行完毕后,返回的值绑定到上。这一部分处理调用方通过方法传入的异常。之外的异常会向上冒泡。 上一篇python协程1:yield的使用介绍了: 生成器作为协程使用时的行为和状态 使用装饰器预激协程 调用方如何使用生成器对象的 .throw(...) 和 .close()...

    vpants 评论0 收藏0
  • python1:yield 10分钟入门

    摘要:协程定义协程的底层架构是在中定义,并在实现的。为了简化,我们会使用装饰器预激协程。执行上述代码结果如下出错的原因是发送给协程的值不能加到变量上。示例使用和方法控制协程。 最近找到一本python好书《流畅的python》,是到现在为止看到的对python高级特性讲述最详细的一本。看了协程一章,做个读书笔记,加深印象。 协程定义 协程的底层架构是在pep342 中定义,并在python2...

    MartinDai 评论0 收藏0
  • 流畅python读书笔记-第十四章-可迭代对象、迭代器和生成器

    摘要:可迭代的对象迭代器和生成器理念迭代是数据处理的基石。可迭代的对象与迭代器的对比从可迭代的对象中获取迭代器标准的迭代器接口有两个方法。此外,也没有办法还原迭代器。最终,函数的定义体返回时,外层的生成器对象会抛出异常这一点与迭代器协议一致。 可迭代的对象、迭代器和生成器 理念 迭代是数据处理的基石。扫描内存中放不下的数据集时,我们要找到一种惰性获取数据项的方式,即按需一次获取一个数据项。这...

    kohoh_ 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<