2020-09-02Python中的协程,为什么说它的底层是生成器?

手把手教你低成本复刻大疆C板缓启动电路(附原理图与选型清单) 本文详细介绍了如何低成本复刻大疆C板缓启动电路,包括核心器件选型、电路搭建与调试实战、参数优化方法论及进阶改进思路。通过替代型号和优化设计,实现功能相近的缓启动电路,成本不到原方案的1/3,适合电子爱好者、在校学生和预算紧张的硬件工程师。 阅读详情

我们曾经在golang关于goroutine的文章当中简单介绍过 协程 的概念,我们再来简单review一下。协程又称为是微线程,英文名是Coroutine。它和线程一样可以调度,但是不同的是线程的启动和调度需要通过操作系统来处理。并且线程的启动和销毁需要涉及一些操作系统的变量申请和销毁处理,需要的时间比较长。而协程呢,它的 调度和销毁都是程序自己来控制 的,因此它更加轻量级也更加灵活。

 

很多人学习python,不知道从何学起。
很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手。
很多已经做案例的人,却不知道如何去学习更加高深的知识。
那么针对这三类人,我给大家提供一个好的学习平台,免费领取视频教程,电子书籍,以及课程的源代码!
QQ群:101677771

协程有这么多优点,自然也会有一些缺点,其中最大的缺点就是 需要编程语言自己支持 ,否则的话需要开发者自己通过一些方法来实现协程。对于大部分语言来说,都不支持这一机制。go语言由于天然支持协程,并且支持得非常好,使得它广受好评,短短几年时间就迅速流行起来。

对于Python来说,本身就有着一个GIL这个巨大的先天问题。GIL是Python的全局锁,在它的限制下 一个Python进程同一时间只能同时执行一个线程 ,即使是在多核心的机器当中。这就大大影响了Python的性能,尤其是在CPU密集型的工作上。所以为了提升Python的性能,很多开发者想出了使用多进程+协程的方式。一开始是开发者自行实现的,后来在Python3.4的版本当中,官方也收入了这个功能,因此目前可以光明正大地说,Python是支持协程的语言了。

生成器(generator)

生成器我们也在之前的文章当中介绍过,为什么我们介绍协程需要用到生成器呢,是因为Python的 协程底层就是通过生成器来实现的 。

通过生成器来实现协程的原因也很简单,我们都知道协程需要切换挂起,而生成器当中有一个yield关键字 ,刚好可以实现这个功能。所以当初那些自己在Python当中开发协程功能的程序员都是通过生成器来实现的,我们想要理解Python当中协程的运用,就必须从最原始的生成器开始。

生成器我们很熟悉了,本质上就是带有yield这个关键词的函数。

def test():
    n = 0
    while n < 10:
        val = yield n 
        print('val = {}'.format(val))
        n += 1

这个函数当中如果没有yield这个语句,那么它就是一个普通的Python函数。加上了val = yield n这个语句之后,它有什么变化呢?

我们尝试着运行一下:

# 调用test函数获得一个生成器
g = test()
print(next(g))
print(next(g))
print(next(g))

得到这么一个结果:

image-20200810104610713

输出的0,1,2很好理解,就是通过next(g)返回的,这个也是生成器的标准用法。奇怪的是为什么val=None呢?val不应该等于n么?

这里想不明白是正常的,因为这里涉及到了一个新的用法就是 生成器的send方法 。当我们在yield语句之前加上变量名的时候,它的含义其实是返回yield之后的内容,再从外界接收一个变量。也就是说当我们执行next(g)的时候,会从获取yield之后的数,当我们执行g.send()时,传入的值会被赋值给yield之前的数。比如我们把执行的代码改成这样:

g = test()
print(next(g))
g.send('abc')
print(next(g))
print(next(g))

我们再来看执行的结果,会发现是这样的:

第一行val不再是None,而是我们刚刚传入的abc了。

队列调度

生成器每次在执行到yield语句之后都会自然挂起,我们可以利用这一点来当做协程来调度。我们可以自己实现一个简易的队列来模拟这个过程。

首先我们声明一个双端队列, 每次从队列左边头部获取任务,调度执行到挂起之后,放入到队列末尾 。相当于我们用循环的方式轮询执行了所有任务,并且这整个全程不涉及任何线程创建和销毁的过程。

class Scheduler:
    def __init__(self):
        self._queue = deque()

    def new_task(self, task):
        self._queue.append(task)

    def run(self):
        while self._queue:
            # 每次从队列左侧获取task
            task = self._queue.popleft()
            try:
                # 通过next执行之后放入队列右侧
                next(task)
                self._queue.append(task)
            except StopIteration:
                pass



sch = Scheduler()
sch.new_task(test(5))
sch.new_task(test(10))
sch.new_task(test(8))
sch.run()

这个只是一个很简易的调度方法,事实上结合上yield from以及send功能,我们还可以实现出更加复杂的协程调度方式。但是我们也没有必要一一穷尽,只需要理解最基础的方法就可以了,毕竟现在我们使用协程一般也不会自己实现了,都会通过官方原生的工具库来实现。

@asyncio.coroutine

在Python3.4之后的版本当中,我们可以通过@asyncio.coroutine这个注解来将一个函数封装成协程执行的生成器。

在吸收了协程这个概念之后,Python 对生成器以及协程做了区分 。加上了@asyncio.coroutine注解的函数称为 协程函数 ,我们可以用iscoroutinefunction()方法来判断一个函数是不是协程函数,通过这个协程函数返回的生成器对象称为 协程对象 ,我们可以通过iscoroutine方法来判断一个对象是不是协程对象。

比如我把刚刚写的函数上加上注解之后再来执行这两个函数都会得到True:

import asyncio

@asyncio.coroutine
def test(k):
    n = 0
    while n < k:
        yield
        print('n = {}'.format(n))
        n += 1
        
print(asyncio.iscoroutinefunction(test))
print(asyncio.iscoroutine(test(10)))

那我们通过注解将方法转变成了协程之后,又该怎么使用呢?

一个比较好的方式是 通过asynio库当中提供的loop工具 ,比如我们来看这么一个例子:

loop = asyncio.get_event_loop()
loop.run_until_complete(test(10))
loop.close()

我们通过asyncio.get_event_loop函数创建了一个调度器,通过调度器的run相关的方法来执行一个协程对象。我们可以run_until_complete也可以run_forever,具体怎么执行要看我们实际的使用场景。

async,await和future

从Python3.5版本开始,引入了async,await和future。我们来简单说说它们各自的用途,其中async其实就是@asyncio.coroutine,用途是完全一样的。同样await代替的是yield from,意为等待另外一个协程结束。

我们用这两个一改,上面的代码就成了:

async def test(k):
    n = 0
    while n < k:
        await asyncio.sleep(0.5)
        print('n = {}'.format(n))
        n += 1

由于我们加上了await,所以每次在打印之前都会等待0.5秒。我们 把await换成yield from也是一样的 ,只不过用await更加直观也更加贴合协程的含义。

Future其实可以看成是一个信号量,我们创建一个全局的future,当一个协程执行完成之后,将结果存入这个future当中。 其他的协程可以await future来实现阻塞 。我们来看一个例子就明白了:

future = asyncio.Future()

async def test(k):
    n = 0
    while n < k:
        await asyncio.sleep(0.5)
        print('n = {}'.format(n))
        n += 1
    future.set_result('success')

async def log():
    result = await future
    print(result)


loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.wait([
    log(),
    test(5)
]))

loop.close()

在这个例子当中我们创建了两个协程,第一个协程是每隔0.5秒print一个数字,在print完成之后把success写入到future当中。第二个协程就是等待future当中的数据,之后print出来。

在loop当中我们要调度执行的不在是一个协程对象了而是两个,所以我们 用asyncio当中的wait将这两个对象包起来 。只有当wait当中的两个对象执行结束,wait才会结束。loop等待的是wait的结束,而wait等待的是传入其中的协程的结束,这就形成了一个依赖循环,等价于这两个协程对象结束,loop才会结束。

async 并不止是可以用在函数上 ,事实上还有很多其他的用法,比如用在with语句上,用在for循环上等等。这些用法比较小众,细节也很多,就不一一展开了,大家感兴趣的可以自行去了解一下。

不知道大家在读这篇文章的过程当中有没有觉得有些费劲,如果有的话,其实是很正常的。原因也很简单,因为Python原生是不支持协程这个概念的,所以在一开始设计的时候也没有做这方面的准备,是后来觉得有必要才加入的。那么作为后面加入的内容,必然会对原先的很多内容产生影响,尤其是协程借助了之前生成器的概念来实现的,那么必然会有很多耦合不清楚的情况。这也是这一块的语法很乱,对初学者不友好的原因。

我建议大家可以 先了解一下go语言当中的协程的概念和用法 再来学习Python当中的async的用法,很多不明白的地方会清晰很多。

05 从零搭建 GPT 模型:LayerNorm、GELU 与解码器堆叠 本文介绍从零搭建GPT模型的关键组件:LayerNorm对特征维归一化,GELU平滑激活,前馈网络扩展收缩,残差连接缓解梯度消失。随后组装TransformerBlock(多头注意力+前馈,Pre-LN),最终构建GPTModel并实现文本生成,附参数量核算。 阅读详情

相关推荐

华为od 面试手撕代码题【完成任务最少小时】

华为od 面试手撕代码题【完成任务最少小时】

MISAYAONE的博客 1424

python底层是c_详解Python中的协程为什么它的底层生成器

作者:承志 来源:掘金|2020-09-03 06:40我们曾经在golang关于goroutine的文章当中简单介绍过协程的概念,我们再来简单review一下。协程又称为是微线程,英文名是Coroutine。它和线程一样可以调度,但是不同的是线程的启动和调度需要通过操作系统来处理。并且线程的启动和销毁需要涉及一些操作系统的变量申请和销毁处理,需要的时间比较长。而协程呢,它的调度和销毁都是...

weixin_39796363的博客 99

yolov5+非机动车违规停放+未标注数据集+机器视觉识别+电动车第一部分

文件内包含的是未标注的非机动车数据集中的电动车第一部分限于上传文件大小,此资源内只包含电动车数据的第一部分,共有3950张图片,电动车数据总共有两部分。所有的资源总共包括自行车图片8000张,已分类,分类包含了山地自行车,公路自行车,越野自行车,通勤自行车,共享单车等。每种自行车的张数大概在800-1000张之间,极个别重复。电动车图片8000张,已分类,分类包含了绿源电动车,台铃电动车,小刀电动车,雅迪电动车,共享电动车等。每种电动车的张数大概在800-1000张之间,极个别重复。三轮车图片6000张,已分类,分类包含了淮海三轮车,闪电客三轮车,金彭三轮车,宗申三轮车,五星三轮车等。每种三轮车的张数大概在500-600张之间,极个别重复。以上资源均为未标注,用于yolov5+未标注非机动车数据集+非机动车违规停放+机器视觉识别若要求已标注数据集资料,博主稍后整理上传

python --- 协程

如果每次返回的数据值不是在一个已有的数据集合中读取的,而是通过程序按照一定的规律计算生成的,那么也就意味着可以不用再依赖一个已有的数据集合。使用asyncio.as_completed(tasks),管理一个协程列表,当任务集合中的某个任务率先执行完毕时,直接通过await关键字返回任务结果,可见返回的结果的顺序也是按照完成任务的顺序排列的。通过上面的代码可以看到,我们不仅可以通过 next() 从生成器中不断获取新的值,还能通过 send() 给生成器传递值,生成器可以根据实际得到值生成新的值。

wyk123_046的博客 1804

PYTHON协程

参考资料 资料 https://blog.csdn.net/andybegin/article/details/77884645 http://python.jobbole.com/86481/ http://python.jobbole.com/87310/ https://segmentfault.com/a/1190000009781688 迭代器 直接作用于for循环的叫可迭...

戈扬的博客 152

python3 协程_python3协程

Python3.5 协程原理本文讲的是Python3.5 协程原理,作为Python核心开发者之一,让我很想了解这门语言是如何运作的。我发现总有一些阴暗的角落我对其中错综复杂的细节不是很清楚,但是为了能够有助于 Python 的一些问题和其整体设计,我觉得我应该试着去理解 Python 的核心语法和内部运作机制。...文章玄学酱2017-10-191246浏览量Python并发编程协程(Cor...

weixin_39723678的博客 420

Python协程深入理解

从语法上来看,协程生成器类似,都是定义体中包含yield关键字的函数。 yield在协程中的用法: 1. 在协程中yield通常出现在表达式的右边 ,例如:datum = yield,可以产出值,也可以不产出--如果yield关键字后面没有表达式,那么生成器产出None. 2. 协程可能从调用方接受数据,调用方是通过send(datum)的方式把数据提供给协程使用,而不是next...

heima201907的博客 202

python协程的作用_协程的作用 Python

原博文2014-12-19 23:57 −1.协程的含义和实现 协程是单进程单线程的超越函数的调度机制,它通过一定的调度手段进行调度。 (Python使用generator机制,greenlet使用汇编控制对程序指向来实现)。 2.协程有什么作用 计算机分为IO bound 和CPU bound两种类型的task。在这两种情况中,协程都没有...相关推荐2017-01-05 18:47 −什么是协...

weixin_39913105的博客 246

Python协程从入门到精通:9个案例解析yield、gevent与asyncio实战

协程技术是Python高并发编程的核心,从yield到asyncio,逐层深入可大幅提升程序性能。

学习成长日记 1489

python制作编程软件的方法_python编程之生成器(generator)

1、生成器介绍首先请确信,生成器就是一种迭代器。生成器拥有next方法并且行为与迭代器完全相同,这意味着生成器也可以用于Python的for循环中。2、生成器函数1)、自定义生成器1 defgenerator1():2 yield 13 yield 24 yield 3567 g =generator1()8 print(next(g))9 print(next(g))10...

weixin_39574065的博客 154

Python -协程:案例-原理简介

Python -协程案例-原理简介 Python协程实现原理 利用Python中yield关键字修饰函数使其成为生成器 利用生成器特点:可迭代且是从yield修饰处开始 协程近似函数调用,因此所用的资源少于进程和线程 可实现并发操作 以下为原理测试代码 def test_1(): while True: print("----1---") time.sleep(1) yield ...

weixin_44093765的博客 740

从零构建Python物体检测模型:基于YOLOv8的完整训练指南

物体检测是计算机视觉领域的核心技术之一,其核心目标是在图像或视频中精准定位并识别目标物体。相较于传统的图像分类(仅判断图像类别)

2601_96229599的博客 229

妈祖文化数字导览系统设计与实现

随着数字文旅行业快速发展,传统文旅景区的线下导览模式已经难以满足当代游客多元化的游览需求。湄洲岛作为妈祖文化的发源地,拥有妈祖祖庙、妈祖文化园、平安塔、海祭广场、文化展馆等大量特色文旅资源,景区景点分散、文化典故厚重,游客普遍存在景点查找困难、历史文化了解不足、游玩路线规划迷茫等现实问题。本文设计并实现一套妈祖文化数字导览系统。

suny8的专栏 159

Agent 记忆压缩通常有哪些方法?

在传统软件中,状态可以通过关系型数据库做毫秒级精准存取;而在 AI Agent 架构中,大模型本身是无状态的概率推理引擎。为了维持多轮交互的连续性,开发者必须将历史记忆注入到上下文(Context)中。上下文爆炸与成本失控:多轮对话与工具调用轨迹(Tool Traces)包含海量冗余 Token,每次 API 请求都需要对全量历史重复计费,成本呈二次方上升。推理延迟(Latency)恶化:超长上下文会导致 GPU 在 Prefill 阶段的计算耗时大幅拉长,严重破坏实时交互体验。

2603_96126046的博客 313

详解 vLLM 开源模型部署全流程

太多团队在部署开源大模型时踩过坑:单卡跑不满吞吐量、多请求排队延迟爆炸、硬件资源利用率不足30%、不同框架适配反复折腾。而vLLM凭借PagedAttention核心设计,已经成为当前工业界部署开源LLM的首选方案,能在几乎不改动业务代码的前提下,把GPU利用率和推理吞吐量提升数倍。今天就从环境准备、基础部署、性能调优到生产级落地,把整套经过线上验证的部署方案完整拆解出来。

nblway的博客 329

22-DataCenter报文序列化

(put在循环后),解析侧遇到时continue不影响store循环——顺序无关但习惯上收尾。

yuhou25的博客 228

可以做年龄编辑的gan对抗网络(SAGAN)

把这 10 张(或更多年龄段)图片按顺序拼接,用 OpenCV 或 FFmpeg 导出为 MP4,就能看到“一个人”的年龄变化过程。:模型只学了 10 个离散年龄段(0~9),直接切换标签会导致五官突然“跳变”,无法生成 18岁→18.5岁 这种细腻变化。如果连续年龄回归,会更加的丝滑,接下来我们将修改一版连续年龄回归的代码,还是在这个的基础之上进行!,比如用预训练的 ArcFace 提取人脸特征,约束不同年龄帧的特征向量尽量接近。,会生成固定噪声下不同年龄的人脸图片,实现“编辑年龄”的效果。

有的话没说出来之前,你是他的主人,一旦说出来你就成了他的奴隶||想要干好事,别太把自己当人,别把别人太不当人||认识这个人就是开了一扇窗户,就能看到不一样的东西,听到不一样的声音,能让你思考、觉悟,这已经够了 223

基于Python的爱奇艺视频数据可视化分析系统

长视频平台剧集、评分、热度与评论数据体量大、维度多,手工汇总难以快速把握类型结构、口碑分布与主演导演影响力。系统采用 B/S 架构,后端入口为app.py,运行端口8060;数据库使用 MySQL(库名iqiyi),经 PyMySQL 访问。前台基于 Bootstrap 5、本地中的 ECharts 5 与 echarts-wordcloud,配合主题。业务表包括usersdramascommentsfavorites。数据可通过导入,或经/ 相关脚本补充样本。分析层在。

qq_35928134的博客 240

2026科学实验能力大赛真题:护林飞行闯关解析

2026 首届全国青少年科学实验能力大赛决赛已于 8 月 20—22 日在济南收官,9 月起各赛区成绩陆续公布。这场白名单赛事鼓励青少年用"做中学"的方式呈现科学探究过程,而图形化编程正是把科普内容变成可交互作品的好工具。今天我们用一道原创项目题「护林飞行闯关」,带大家用 Scratch 做一个科普护林小游戏,把六大考点串成能玩的作品。

IT_Scratch的博客 208

Java深入解析篇二十之JavaStream API详解

Stream(流)是引入的数据处理抽象,位于包。它表示从数据源产生的元素序列,并支持对其进行函数式、聚合式操作。不是集合:流不存储数据,只描述对数据的计算;不是 IO 流:与无关;惰性管道:中间操作只是登记,终端操作才触发实际计算。// 命令式写法(对照) // List<String> r = new ArrayList<>();

萧瑟余晖的博客 347

whisper如何打包使用

Whisper作为一款强大的离线语音识别工具,能够在无网络环境下提供快速、准确的语音转文字服务。对于开发者而言,了解如何将Whisper打包并集成到自己的项目中至关重要。

2601_96210038的博客 48
上一篇: Python采集CSDN博客排行榜数据
下一篇: python爬取B站视频弹幕分析并制作词云
Python_sn
博客等级 码龄6年 316粉丝 290原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值