首页 > 编程语言 >Python 异步编程之yield关键字

Python 异步编程之yield关键字

时间:2023-12-15 17:45:36浏览次数:28  
标签:异步 ... Python get 生成器 yield next send

image

背景介绍

在前面的篇章中介绍了同步和异步在IO上的对比,从本篇开始探究python中异步的实现方法和原理。
python协程的发展流程:

  • python2.5 为生成器引用.send()、.throw()、.close()方法
  • python3.3 为引入yield from,可以接收返回值,可以使用yield from定义协程
  • Python3.4 加入了asyncio模块
  • Python3.5 增加async、await关键字,在语法层面的提供支持
  • python3.7 使用async def + await的方式定义协程
  • 此后asyncio模块更加完善和稳定,对底层的API进行的封装和扩展
  • python将于 3.10版本中移除 以yield from的方式定义协程

yield 简介

yield 通常是使用在生成器函数中。当一个函数中有yield关键字,那么该函数就不是一个普通函数而是一个生成器函数。

>>> def get_num():
...     for i in range(5):
...         yield i
...
>>> g = get_num()
>>> type(g)
<class 'generator'>
>>>
>>> for i in g:
...     print(i)
...
0
1
2
3
4

调用get_num生成了一个生成器g,通过type可以看到g是一个生成器类型。生成器是一种迭代器,可以通过for循环迭代出数据。

以上是yield的第一种使用方法,其实yeild出了可以作为生成器的关键字,也可以实现协程。在实现协程之前首先需要学习yield的基础使用。

next 取值

yield实现的生成器是一种迭代器,所有的迭代器都可以通过next取值。

>>> def get_num():
...     for i in range(5):
...         yield i
...
>>> g = get_num()
>>>
>>> next(g)
0
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
4
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>>

执行过程
使用next会让get_num从头开始执行,遇到yield i时,返回i给调用者,并暂停在yield i这一行,等待下一个next取值的到来,从yield i这里继续执行。这种能够暂停执行,恢复执行的能力是生成器的一种特性,也就是这种特性可以实现协程。
特点:
使用next取值有两个特点:

  1. 只能从前向后取值,每次只能取一个
  2. 迭代器中没有值时再通过next取值会报错

send 发送值

调用包含yield的函数会返回一个生成器generator,可以通过next从生成器中不断取值,通过send也可以将数值送到生成器中。如下:

>>> def get_num():
...     for i in range(5):
...         temp = yield i
...         print(temp)
...
>>> g = get_num()
>>> next(g)
0
>>> g.send(100)
100
1
>>>

执行过程
next让程序执行到temp = yield i,返回i给调用者并暂停在这里。g.send(100) 从temp = yield i开始执行,将100传递给yield i,并让代码继续执行直到遇到下一个yield,返回yield 后面的数值。

send可以将值传递给生成器,next是从生成器中取值,两者目的不一致,但是也相同的能力,那就是可以驱动程序从一个yield执行到下一个yield。如再次执行send,就会从上一次暂停的地方继续执行到下一个yield处。

>>> g = get_num()
>>> next(g)
0
>>> g.send(100)
100
1
>>> g.send(200)
200
2

特点

  1. 将值传送到生成器中
  2. 驱动生成器执行

启动生成器

生成器创建之后需要启动才能返回值,也就从代码第一行执行到yield处,需要一个事件去驱动代码执行。两种方式可以启动,分别是send和next

>>> g = get_num()
>>> next(g)
0
>>> g = get_num()
>>> g.send(None)
0

next:
程序从第一行执行到 temp = yield i暂停。
send:
send()必须传入关键字None,其他值会报错。因为send是从yield 处开始执行,由于启动程序不是yield语句开始,所有不能传值。
close 结束迭代
通常来说不要手动接受生成器,因为生成器迭代完成之后就会被释放。但是也可以通过close的方法结束生成器的迭代。

>>> g = get_num()
>>> g.send(None)
0
>>>
>>>
>>>
>>> g = get_num()
>>>
>>> next(g)
0
>>> next(g)
None
1
>>> g.send(100)
100
2
>>> g.close()
>>> g.close()
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

生成器的return

在前面的示例中或许你也发现了,使用next取值,当取完所有元素之后再次取值时会抛出异常。根本原因是程序执行到return了。在生成器中执行到return会抛出StopIteration异常

>>> def gen():
...     yield 100
...     return 200
...     yield 300
...
>>> g = gen()
>>> next(g)
100
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration: 200
>>> next(g)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration    
程序执行到return时会抛出StopIteration异常,终止程序。yield 300永远也不会执行。
调用生成器中无法直接获取到return的返回值,可以通过捕获异常的方法获取return的返回值。
>>> try:
...     next(g)
...     next(g)
... except StopIteration as e:
...     result = e.value
...     print(result)
...
100
200

使用try except 捕获 StopIteration 异常之后,从中取出value就是返回值。

生成器实现的协程

生成器通常用作迭代器,但是也可以用作协程。协程其实就是生成器函数,通过主体中含有 yield 关键字的函数创建。注意这里只是为了探究原理,真实情况下协程不是使用yield。
协程的类似于函数调用,函数A调用函数B,B执行完成之后A继续执行。这个过程不涉及CPU调度。
下面通过生产者,消费者模型来说明yield如何实现协程。

import time


def consume():
    r = ''
    while True:
        n = yield r
        print(f'[consumer] 开始消费 {n}...')
        time.sleep(1)
        r = f'{n} 消费完成'


def produce(c):
    next(c)
    n = 0
    while n < 5:
        n = n + 1
        print(f'[producer] 生产了 {n}...')
        r = c.send(n)
        print(f'[producer] consumer return: {r}')
    c.close()


if __name__=='__main__':
    c = consume()
    produce(c)

执行输出:

[producer] 生产了 1...
[consumer] 开始消费 1...
[producer] consumer return: 1 消费完成
[producer] 生产了 2...
[consumer] 开始消费 2...
[producer] consumer return: 2 消费完成
[producer] 生产了 3...
[consumer] 开始消费 3...
[producer] consumer return: 3 消费完成
[producer] 生产了 4...
[consumer] 开始消费 4...
[producer] consumer return: 4 消费完成
[producer] 生产了 5...
[consumer] 开始消费 5...
[producer] consumer return: 5 消费完成

执行过程:

  1. c = consume() 创建消费者生成器
  2. produce(c)将消费者生成器传递到生产者函数中,生产者会负责驱动消费者
  3. next(c)驱动生产者启动,send(None)也可以完成
  4. 生产者在while中自增n,并调用 r = c.send(n) 将n传递给消费者
  5. 消费者n = yield r接收到n,用time.sleep模拟睡眠,给返回值r赋值,运行到下一个n = yield r暂停,返回r给生产者
  6. 生产者从暂定的r = c.send(n)恢复执行
  7. 直到n<5,生产者退出之后,整个协程退出。

这个过程中,主要配合的就是r = c.send(n)和 n = yield r这两个关键点。两行代码在执行时可以暂停,驱动另外一个执行。这里体现的协程的一个特点:主动让出CPU,协助式执行而不是线程那种CPU抢占式。
image

标签:异步,...,Python,get,生成器,yield,next,send
From: https://www.cnblogs.com/goldsunshine/p/17902321.html

相关文章

  • 12.15---python文件读取
    withopen('pi_digits.txt')asfile:contents=file.read()print(contents.strip())要想访问文件内容需要先打开它才能访问,函数open()接受一个参数:要打开文件的名称。在当前执行文件的目录中查找文件名。代码中,open('E:/python/文件和异常/pi_digits.txt')返回一个表示......
  • python二分类模型精度低怎么办
    在二分类模型中,如果模型的精度较低,可能需要采取一些措施来改进模型性能。本文将介绍一些常见的方法和技巧,帮助提高二分类模型的精度。1.数据预处理确保对数据进行适当的预处理是提高模型精度的重要步骤。常见的数据预处理方法包括:-数据清洗:处理缺失值、异常值等。-特征选择:选择对目......
  • 如何在 python 中安装 torch
    PyTorch是一款功能强大的深度学习框架,它提供了丰富的工具和接口来支持各种深度学习任务。本文将介绍在Python中安装PyTorch的步骤和方法,以帮助读者快速开始使用PyTorch。1.安装Python首先,确保你的计算机上已经安装了Python。建议使用Python的最新版本,可以从官方下载并安装Python。2......
  • 在Python的类型提示中,你不能直接使用​​or​​​来表示一个参数可以是多种类型中的一
    在Python的类型提示中,你不能直接使用or来表示一个参数可以是多种类型中的一种。你应该使用typing.Union来表示这种情况¹²。所以,你的函数应该这样写:fromtypingimportUnion,Listdefquery_coilNum(self,coilNum:Union[str,List[str]]):pass在这个例子中,Union[str,Li......
  • 【Lidar】基于Python格网法计算点云体积(eg.树木体积)
    ​        这两天一直不在状态,不是特别想分享文章,所以也没怎么更新。但是代码放在文件里始终不是它的归宿,只有被不断使用它才能进步,才能诠释它的意义。所以今天抽空给大家分享一下如何基于Python利用格网法计算点云的体积,我这里是做林业的点云,所以是按照树木体积编写的......
  • python读取rtsp流,并消耗
    1.python读取rtsp流,并消耗(用线程)importosimportcv2importgcimporttimeimportthreadingimportnumpyasnpfromPILimportImagetop=100stack=[]#向共享缓冲栈中写入数据:defwrite(stack,cam,top:int)->None:  """  :paramcam:摄像头参数  :para......
  • Python实现RTSP流测试
    本文将详细介绍如何使用Python来测试RTSP流。首先,我们需要了解什么是RTSP。RTSP(RealTimeStreamingProtocol)是一种基于文本的IP协议,主要用于控制实时数据的传输,例如音频或视频。在本文中,我们将使用Python中的OpenCV库来测试RTSP流。一、安装OpenCV库在Python中使用OpenCV库需......
  • python之typing
    typing介绍Python是一门动态语言,很多时候我们可能不清楚函数参数类型或者返回值类型,很有可能导致一些类型没有指定方法,在写完代码一段时间后回过头看代码,很可能忘记了自己写的函数需要传什么参数,返回什么类型的结果,就不得不去阅读代码的具体内容,降低了阅读的速度,typing模块可以很......
  • python 脚本的启动模式(python -m以模块方式启动)
    今天再看python的项目时,发现GitHub中给出的python脚本的执行格式是python-mpipinstallsomepackage。于是开始了python模式启动之旅。其中很多相关借鉴了该博客,同时感谢博主:http://www.cnblogs.com/xueweihan/p/5118222.html什么是python启动模块:通过python启动一个库中......
  • python远程关闭window电脑
    背景公司的电脑机器太多,每次关闭的时候需要一台一台的关闭,比较麻烦,因此做一个批量关闭的功能Windows电脑不想liunx有ssh工具,因此需要事前在Windows电脑上面安装一个ssh工具。1、Github上下载OpenSSH包下载地址 https://github.com/PowerShell/Win32-OpenSSH/releases2......