Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现

generator,生成器,入门,coroutine,原生,登峰造极,python3,并发,异步,编程,async,底层,实现 · 浏览次数 : 401

小编点评

**生成器和协程的区别:** * **生成器:**是一种特殊的迭代器,它可以暂停并在恢复执行时保留它的状态。 * **协程:**可以被另一个函数(调用方)恢复执行,并在执行时暂停并在任意时刻恢复执行。 **生成器的工作原理:** 1. 生成器函数使用 `yield` 语句来暂停执行并返回结果。 2. 当生成器调用 `send` 方法时,它会从 `yield` 语句处恢复执行,并将传入的参数赋值给变量 `x`。 3. 生成器函数会不断调用 `send` 方法,并将返回值赋值给 `x`。 4. 生成器函数最终返回 `x` 值。 **协程的应用场景:** * **数据IO操作:**例如从本地往服务器传输数据。 * **回调机制:**例如在网络连接断开时执行回调函数。 * **复杂控制流:**例如在执行多个任务时管理资源分配。

正文

普遍意义上讲,生成器是一种特殊的迭代器,它可以在执行过程中暂停并在恢复执行时保留它的状态。而协程,则可以让一个函数在执行过程中暂停并在恢复执行时保留它的状态,在Python3.10中,原生协程的实现手段,就是生成器,或者说的更具体一些:协程就是一种特殊的生成器,而生成器,就是协程的入门心法。

协程底层实现

我们知道,Python3.10中可以使用async和await关键字来实现原生协程函数的定义和调度,但其实,我们也可以利用生成器达到协程的效果,生成器函数和普通函数的区别在于,生成器函数使用 yield 语句来暂停执行并返回结果。例如,下面是一个使用生成器函数实现的简单协程:

def my_coroutine():  
    while True:  
        x = yield  
        print(x)  
  
# 使用生成器函数创建协程  
coroutine = my_coroutine()  
  
# 启动协程  
next(coroutine)  
  
# 在协程中传入数据  
coroutine.send(1)  
coroutine.send(2)  
coroutine.send(3)

程序返回:

➜  mydemo git:(master) ✗ /opt/homebrew/bin/python3.10 "/Users/liuyue/wodfan/work/mydemo/src/test.py"  
1  
2  
3

在上面的代码中,生成器函数 my_coroutine 使用了一个无限循环来实现协程的逻辑。每当调用 send 方法时,协程就会从 yield 语句处恢复执行,并将传入的参数赋值给变量 x。

如此,就完成了协程执行-》阻塞-》切换-》回调的工作流模式。

当然,作为事件循环机制,协程服务启动可能无限期地运行,要关闭协程服务,可以使用生成器的close()方法。当一个协程被关闭时,它会生成GeneratorExit异常,该异常可以用生成器的方式进行捕获:

def my_coroutine():  
    try :  
        while True:  
            x = yield  
            print(x)  
    except GeneratorExit:  
            print("协程关闭")  
  
# 使用生成器函数创建协程  
coroutine = my_coroutine()  
  
# 启动协程  
next(coroutine)  
  
# 在协程中传入数据  
coroutine.send(1)  
coroutine.send(2)  
coroutine.send(3)  
  
coroutine.close()

程序返回:

➜  mydemo git:(master) ✗ /opt/homebrew/bin/python3.10 "/Users/liuyue/wodfan/work/mydemo/src/test.py"  
1  
2  
3  
协程关闭

业务场景

在实际业务场景中,我们也可以使用生成器来模拟协程流程,主要体现在数据的IO流操作中,假设我们需要从本地往服务器传输数据,首先建立链接对象:

class Connection:  
  
  
    def __init__(self, addr):  
        self.addr = addr  
  
    def transmit(self, data):  
        print(f"X: {data[0]}, Y: {data[1]} sent to {self.addr}")

随后建立生成器函数:

def send_to_server(conn):  
    while True:  
        try:  
            raw_data = yield  
            raw_data = raw_data.split(' ')  
            coords = (float(raw_data[0]), float(raw_data[1]))  
            conn.transmit(coords)  
        except ConnectionError:  
            print("链接丢失,进行回调")  
            conn = Connection("重新连接v3u.cn")

利用生成器调用链接类的transmit方法进行数据的模拟传输,如果链接断开,则会触发回调重新连接,执行逻辑:

if __name__ == '__main__':  
  
  
    conn = Connection("v3u.cn")  
  
    sender = send_to_server(conn)  
    sender.send(None)  
  
    for i in range(1, 6):  
        sender.send(f"{100/i} {200/i}")  
  
    # 模拟链接断开  
    conn.addr = None  
  
  
    sender.throw(ConnectionError)   
  
    for i in range(1, 6):  
        sender.send(f"{100/i} {200/i}")

程序返回:

X: 100.0, Y: 200.0 sent to v3u.cn  
X: 50.0, Y: 100.0 sent to v3u.cn  
X: 33.333333333333336, Y: 66.66666666666667 sent to v3u.cn  
X: 25.0, Y: 50.0 sent to v3u.cn  
X: 20.0, Y: 40.0 sent to v3u.cn  
链接丢失,进行回调  
X: 100.0, Y: 200.0 sent to 重新连接v3u.cn  
X: 50.0, Y: 100.0 sent to 重新连接v3u.cn  
X: 33.333333333333336, Y: 66.66666666666667 sent to 重新连接v3u.cn  
X: 25.0, Y: 50.0 sent to 重新连接v3u.cn  
X: 20.0, Y: 40.0 sent to 重新连接v3u.cn

如此,我们就可以利用生成器的“状态保留”机制来控制网络链接突然断开的回调补救措施了。

所以说,协程就是一种特殊的生成器:

async def test():  
    pass  
  
print(type(test())) 

您猜怎么着?

<class 'coroutine'>

结语

诚然,生成器和协程也并非完全是一个概念,与生成器不同的是,协程可以被另一个函数(称为调用方)恢复执行,而不是只能由生成器本身恢复执行。这使得协程可以用来实现更复杂的控制流,因为它们可以在执行时暂停并在任意时刻恢复执行。

与Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现相似的内容:

Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现

普遍意义上讲,生成器是一种特殊的迭代器,它可以在执行过程中暂停并在恢复执行时保留它的状态。而协程,则可以让一个函数在执行过程中暂停并在恢复执行时保留它的状态,在Python3.10中,原生协程的实现手段,就是生成器,或者说的更具体一些:协程就是一种特殊的生成器,而生成器,就是协程的入门心法。 协程底

3.0 Python 迭代器与生成器

当我们需要处理一个大量的数据集合时,一次性将其全部读入内存并处理可能会导致内存溢出。此时,我们可以采用迭代器`Iterator`和生成器`Generator`的方法,逐个地处理数据,从而避免内存溢出的问题。迭代器是一个可以逐个访问元素的对象,它实现了`python`的迭代协议,即实现了`__iter__()`和`__next__()`方法。通过调用`__next__()`方法,我们可以逐个访问迭代

把Mybatis Generator生成的代码加上想要的注释

作者:王建乐 1 前言 在日常开发工作中,我们经常用Mybatis Generator根据表结构生成对应的实体类和Mapper文件。但是Mybatis Generator默认生成的代码中,注释并不是我们想要的,所以一般在Generator配置文件中,会设置不自动生成注释。带来的问题就是自动生成代码之

关于对于Java中Entity以及VO,以及DTO中Request对象序列化的学习

关于 Serializable的探讨 前提引入 是由于软件测试上有同学提到说,什么该字段在程序刚运行时,导致jvm激增,所以吸引了我的注意 回顾代码 MybatisPlus Generator自动生成的entity中就经常带有这个, 而且我在开发代码的时候VO,以及DTO常常是直接复制对应的enti

Jenkins Pipeline 流水线 - Parameters 参数化构建

可以通过参数的方式,指定构建的版本 有两种方式 界面添加 Pipeline Script 脚本配置 (需要Build 一次,然后生效,不知道有没有其它办法) General 界面添加 Pipeline Script Declarative Directive Generator pipeline {

终于搞懂了!原来 Vue 3 的 generate 是这样生成 render 函数的

前言 在之前的 面试官:来说说vue3是怎么处理内置的v-for、v-model等指令? 文章中讲了transform阶段处理完v-for、v-model等指令后,会生成一棵javascript AST抽象语法树。这篇文章我们来接着讲generate阶段是如何根据这棵javascript AST抽象

AIGC的行业发展

1. AIGC的行业发展 AIGC(Artificial Intelligence Generated Content,人工智能生成内容)是利用人工智能技术来自动生成内容的一种新型内容创作方式。它基于生成对抗网络(GAN)、大型预训练模型等技术,通过对已有数据进行学习和模式识别,以适当的泛化能力生成

Unity热更学习toLua使用--[1]toLua的导入和默认加载执行lua脚本

[0]toLua的导入 下载toLua资源包,访问GitHub项目地址,点击下载即可。 将文件导入工程目录中: 导入成功之后会出现Lua菜单栏,如未成功生成文件,可以点击Generate All 重新生成(注意很可能是路径问题导致的生成失败!) 之后就可以开始编写脚本执行第一个lua程序了! [1]

利用英特尔 Gaudi 2 和至强 CPU 构建经济高效的企业级 RAG 应用

检索增强生成 (Retrieval Augmented Generation,RAG) 可将存储在外部数据库中的新鲜领域知识纳入大语言模型以增强其文本生成能力。其提供了一种将公司数据与训练期间语言模型学到的知识分开的方式,有助于我们在性能、准确性及安全隐私之间进行有效折衷。 通过本文,你将了解到英特

rerank来提升RAG的准确度的策略

RAG(Retrieval-Augmented Generation)是一种结合检索和生成两种技术的模型,旨在通过检索大规模知识库来增强文本生成任务的准确性。 要通过reranking(重排序)来提升RAG的准确度,可以采取以下策略: 1. 使用更精细的评分函数 RAG通常会在检索阶段根据输入问题或