《Fluent Python》笔记 | 协程

news2024/11/24 16:41:27

生成器作为协程

协程是指一个过程, 这个过程与调用方协作, 产出由调用方提供的值。

协程使用的简单演示(用作协程的生成器):

>>> def simple_coroutine(): # 生成器函数
... print('-> coroutine started')
... x = yield # yield表达式右边为协程产出的值,没有默认为None
... print('-> coroutine received:', x)
...
>>> my_coro = simple_coroutine() # 得到生成器对象
>>> my_coro 
<generator object simple_coroutine at 0x100c2be10>
>>> next(my_coro) # 启动生成器,到第一个yield处暂停
-> coroutine started
>>> my_coro.send(42) # 向协程中发送数据,协程定义体中yield表达式会计算出42
-> coroutine received: 42
Traceback (most recent call last): # 
...
StopIteration

协程有四个状态,协程当前的状态可以使用inspect.getgeneratorstate() 函数确定, 该函数会返回下述字符串中的一个。
‘GEN_CREATED’:等待开始执行。
‘GEN_RUNNING’:解释器正在执行。
‘GEN_SUSPENDED’:在 yield 表达式处暂停。
‘GEN_CLOSED’:执行结束。

因为 send 方法的参数会成为暂停的 yield 表达式的值, 所以, 仅当协程处于暂停状态(‘GEN_SUSPENDED’)时才能调用 send 方法,否则会报错。

生成器实例化后得到的协程my_coro处于’GEN_CREATED’状态,通过next(my_coro) (也可以调用my_coro.send(None),效果相同)激活协程变为’GEN_RUNNING’状态(第一次次激活叫做预激)。运行到yield表达式变为’GEN_SUSPENDED’状态,协程定义体执行结束变为’GEN_CLOSED’状态。

通过装饰器预激协程

因为预激是使用协程的关键步骤,为了简化协程的用法,有时会使用一个预激装饰器,这样可以避免忘记预激操作。预激装饰器示例:

from functools import wraps

def coroutine(func):
"""装饰器:向前执行到第一个`yield`表达式, 预激`func`"""
	@wraps(func)
	def primer(*args,**kwargs):	# 将被装饰的生成器函数替换成函数primer,返回预激后的生成器
        gen = func(*args,**kwargs)	# 获取生成器对象
        next(gen)		# 预激
        return gen		# 返回预激后的生成器
    return primer

终止协程和异常处理

未处理的异常会导致协程终止。

示例代码如下:

class DemoException(Exception):
"""为这次演示定义的异常类型。 """

def demo_exc_handling():
    print('-> coroutine started')
    try:
    	while True:
            try:
                x = yield
            except DemoException:
                print('*** DemoException handled. Continuing...')
            else:
                print('-> coroutine received: {!r}'.format(x))
    finally:
		pass # 在协程终止时执行的操作
>>> exc_coro = demo_exc_handling() # 获取作为协程的生成器对象
>>> next(exc_coro)		# 预激协程
-> coroutine started
>>> exc_coro.send(11)	# 发送数据给协程,通过send函数发送给协程的值会被yield表达式接收
-> coroutine received: 11
>>> exc_coro.send(22)
-> coroutine received: 22
>>> from inspect import getgeneratorstate
>>> exc_coro.throw(DemoException)
*** DemoException handled. Continuing...
>>> getgeneratorstate(exc_coro)
'GEN_SUSPENDED'
>>> exc_coro.close()
>>> getgeneratorstate(exc_coro)
'GEN_CLOSED

重要方法:

  • generator.send(...)

    如果发送给协程的值(通常可以使用内置的 NoneEllipsis)在协程定义体中参与运算抛出异常,且这个抛出的异常未处理,那么就会导致协程终止。

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

    使协程在暂停的 yield 表达式处抛出指定的异常(exc_type)。 如果协程处理了抛出的异常, 代码会向前执行到下一个 yield 表达式, 而产出的值会成为调用 generator.throw方法得到的返回值。 如果协程没有处理抛出的异常, 异常会向上冒泡, 传到调用方的上下文中。并且协程也会终止。

  • generator.close()

    致使协程在暂停的 yield 表达式处抛出 GeneratorExit 异常。如果协程没有处理这个异常, 或者抛出了 StopIteration 异常(通常是指运行到结尾),调用方不会报错(此时协程终止退出)。 如果收到 GeneratorExit 异常, 协程一定不能产出值, 否则解释器会抛出 RuntimeError 异常。协程抛出的其他异常会向上冒泡, 传给调用方。并且协程也会终止。

虽然上面所说的协程其实本质上是生成器对象,此时生成器对象的行为体现了协程。

yield from在协程中的运用

yield from的主要功能是打开双向通道, 把最外层的调用方与最内层的子生成器连接起来, 这样二者可以直接发送和产出值, 还可以直接传入异常,而不用在位于中间的协程中添加大量处理异常的样板代码。 有了这个结构, 协程可以通过以前不可能的方式委托职责。

在这里插入图片描述

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

  • 子生成器
    yield from 表达式中 <iterable> 部分获取的生成器。

该结构运行流程如下:

委派生成器在 yield from 表达式处暂停时, 调用方可以直接把数据发给子生成器, 子生成器再把产出的值发给调用方。 子生成器返回之后, 解释器会抛出 StopIteration 异常, 并把返回值附加到异常对象上, 此时委派生成器会恢复。

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) # 终止协程
    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)
9 boys averaging 40.42kg
9 boys averaging 1.39m
10 girls averaging 42.04kg
10 girls averaging 1.43m

grouper 发送的每个值都会经由 yield from 处理, 通过管道传给averager 实例。 grouper 会在 yield from 表达式处暂停, 等待averager 实例处理客户端发来的值。 averager 实例运行完毕后, 返回的值绑定到 results[key] 上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/89544.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JavaScript系列之通过babel体验ES6模块化

文章の目录一、创建项目文件夹二、打开cmd窗口三、初始化项目四、安装依赖模块五、项目根目录创建文件六、在babel.config.js 文件中添加如下配置七、编写代码八、执行代码九、相关项目依赖写在最后一、创建项目文件夹 名称不要使用中文&#xff0c;不能使用 babel&#xff0c…

[附源码]计算机毕业设计的旅游景点管理系统的设计与实现Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; Springboot mybatis MavenVue等等组成&#xff0c;B/S模式…

VS Code —— 介绍如何配置快捷代码片段和一些自用插件

VS Code —— 介绍如何配置快捷代码片段和一些自用插件 《工欲善其事&#xff0c;必先利其器》—— 既然点进来了&#xff0c;麻烦你看下去&#xff0c;希望你有不一样的收获~ 一、配置代码片段 打开 VS Code&#xff0c;输入快捷键 Ctrl Shift p&#xff0c;打开面板&#…

继真人秀后的又一次大赛,万应低代码一路向前

12月8日&#xff0c;凛冬的长沙&#xff0c;比赛现场暖气充足&#xff0c;11 个参赛团队的队长正在台下跃跃欲试&#xff0c;本届“万应杯”低代码应用开发大赛已经开启月余&#xff0c;大家都很期待能在淘汰赛上一展身手。 他们手上的项目&#xff0c;涉及到建筑、园区、生鲜…

30、基于51单片机的数字电压表(ADC0809)(Proteus仿真+程序)

编号&#xff1a;30 基于51单片机的数字电压表&#xff08;ADC0809&#xff09; 功能描述&#xff1a; 本设计由51单片机最小系统ADC0809模块八路路模拟量输入模块12864显示模块 1、主控制器是89C52单片机 2、ADC0809模数转换器进行A/D转换&#xff0c;读取电压八路数据&…

现在转行码农的成本已经非常高了,别盲目转行..

转行码农一直是个比较火热的话题&#xff0c;也有很多读者咨询过这个问题&#xff0c;转成功的也不少&#xff0c;比如下面这位香港的同学&#xff1a; 这位朋友半年前就跟我聊过&#xff0c;他不太想干没有技术含量的体力活&#xff0c;一直在坚持自学&#xff0c;这也算如愿…

软件测试基础知识总结(面试临时抱佛脚)

之前有将基础的软件测试知识做了一个总结&#xff0c;但比较潦草&#xff0c;很多内容只是一笔带过&#xff0c;快到年底了&#xff0c;自己也有个写年终知识总结文档的计划&#xff0c;就将基础的理论知识重新整理一番。。。 有人问我&#xff0c;这些都是能搜索到的知识&…

65-82-springcloud-gateway-config-bus

65-82-springcloud-gateway-config-bus&#xff1a; Gateway gateway官网&#xff1a;https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1.RELEASE/reference/html/ 1、什么是gateway Gateway是在Spring生态系统之上构建的API网关服务&#xff0c;基…

c++引用

1.什么是c引用&#xff1f; 引用是c对c的重要扩充。c中新增了引用的概念&#xff0c;引用可以作为一个已定义变量的别名。 #include "stdafx.h" #include <iostream> using namespace std; // 1.引用的基本使用 void test01(){int a 10;// 给变量a取一个别名…

金仓数据库KingbaseES 归档日志清理

WAL是Write Ahead Log的简写&#xff0c;和Oracle的redo日志类似&#xff0c;在R3版本存放在data/sys_log中&#xff0c;R6版本以后在data/sys_wal目录&#xff0c;在数据库访问过程中&#xff0c;任何对数据块的修改都会记录到wal日志&#xff0c;并写入到wal文件保存到磁盘&a…

PMP有没有必要续证呢?

在还只看到标题的时候&#xff0c;我当时就觉得必须续啊&#xff0c;为什么不续&#xff0c;我花了那么多时间精力和钱财去考的&#xff0c;我自然得去给它续上&#xff0c;不然白拿了&#xff0c;才拿了三年我还没捂热就给我失效了多不值。 首先美国PMI要求PMP证书是三年一换…

面试题 :Unity编辑器基础

1、请描述游戏动画有几种&#xff0c;以及其原理。 关键帧动画&#xff1a;每一帧动画序列当中包含了顶点的空间位置信息以及改变量&#xff0c;然后通过插值运算&#xff0c;得出动画效果。选中某一游戏对象&#xff0c;创建animation&#xff0c;添加属性Transform&#xff0…

【Meetup 预告】OpenMLDB + MaxCompute:集成打通云上生态,高效构建 AI 应用

2022年12月3日&#xff08;周六&#xff09;上午10&#xff1a;00-12:00&#xff0c;开源机器学习数据库 OpenMLDB 第八期 Meetup 将通过线上直播的形式展开。 活动背景 数据的爆发式增长为 AI 应用的繁荣提供了坚实的基础&#xff0c;而云服务作为新一代快速整合、高效计算的…

STC-Seg:首个超越PointTrack的弱监督视频MOTS算法

弱监督视频多目标实例分割新SOTA&#xff08;代码已开源&#xff09;&#xff1a; Paper: Solve the Puzzle of Instance Segmentation in Videos: A Weakly Supervised Framework with Spatio-Temporal Collaboration Code: https://github.com/ylqi/STC-Seg 众所周知&#xf…

linux之syslog使用说明

syslog 系统日志应用 1) 概述 syslog默认的日志守护进程。默认的syslog配置文件是/etc/syslog.conf文件。程序&#xff0c;守护进程和内核提供了访问系统的日志信息。因此&#xff0c;任何希望生成日志信息的程序都可以向 syslog 接口呼叫生成该信息。 几乎所有的网络设…

【2台真机实战--Redis一主一从两哨兵配置集群和主从切换】

2台真机实战--Redis一主一从两哨兵配置集群和主从切换前言实战真实环境节点分布配置主服务器&#xff08;192.168.137.23&#xff09;配置redis.conf配置sentinel.conf从服务器&#xff08;192.168.137.24&#xff09;配置redis.conf配置sentinel.conf启动redis整合SpringBoot配…

[附源码]Node.js计算机毕业设计服装销售商城系统Express

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

python之路 socket、socket server

一、socket socket的英文原义是“孔”或“插座”。作为BSD UNIX的进程通信机制&#xff0c;取后一种意思。通常也 称作"套接字"&#xff0c;用于描述IP地址和端口&#xff0c;是一个通信链的句柄&#xff0c;可以用来实现不同虚拟机或不同计算机之间的通信。在Inter…

nodejs+vue大学生企业推荐系统vue

1、 node_modules文件夹(有npn install产生) 这文件夹就是在创建完项目后&#xff0c;cd到项目目录执行npm install后生成的文件夹&#xff0c;下载了项目需要的依赖项。 2、package.json文件 此文件是项目的配置文件&#xff08;可定义应用程序名&#xff0c;版本&am…

Java基础之并发理论基础

Java基础之并发理论基础一、为什么需要多线程二、线程不安全1、三要素之一可见性&#xff08;CPU缓存引起&#xff09;2、三要素之一原子性&#xff08;分时复用引起&#xff09;3、三要素之一有序性&#xff08;重排序引起&#xff09;一、为什么需要多线程 CPU 增加了缓存&a…