当前位置:首页 > 数码 > Python协程的成功形式 (python怎样打开)

Python协程的成功形式 (python怎样打开)

admin7个月前 (05-03)数码46

协程是/target=_blankclass=infotextkey>Python中弱小的并发编程工具,准许开发者编写异步代码以提高程序的性能和效率。在本文中,咱们将深化讨论Python中协程的成功形式,包含生成器、asyncio库和async/awt关键字。咱们还会提供具体的示例代码,协助您了解和运行协程。

什么是协程?

协程是一种轻量级的线程,它准许程序在口头时启动切换,而无需创立额外的线程或进程。这种十分适用于I/O密集型义务,例如网络恳求、文件读写和数据库查问,由于它们经常会造成程序阻塞期待照应。

协程准许程序在期待I/O操作成功时,立刻切换到其余义务,从而提高了程序的并发性。这使得Python能够高效地处置少量并发恳求,而无需为每个恳求创立一个线程或进程。

协程的成功形式

1.生成器(Generator)

生成器是Python中协程的最早成功形式之一。经过经常使用yield关键字,函数可以暂停口头并将控制权前往给调用者,而后在须要时复原口头。这使得生成器成为一种可迭代的数据结构,也可以用于成功协程。

以下是一个便捷的生成器示例,展示了如何经常使用生成器成功协程:

defsimple_coroutine():print("Start")x=yieldprint("Received:",x)#创立协程对象coroutine=simple_coroutine()#启动协程next(coroutine)#输入:Start#发送数据到协程coroutine.send(42)#输入:Received:42

在这个示例中,simple_coroutine是一个生成器函数,它经过yield关键字挂起口头。首先创立了协程对象,并经常使用next()函数启动它。而后,经过send()方法向协程发送数据,协程会继续口头并输入接纳到的数据。

2.asyncio库

Python的规范库提供了asyncio模块,它是异步I/O操作的框架,用于治理协程。asyncio库引入了事情循环,它准许多个协程在非阻塞的状况下并发运转。

上方是一个经常使用asyncio库的示例:

importasyncioasyncdefhello_world():awaitasyncio.sleep(1)print("Hello,World!")#创立事情循环loop=asyncio.get_event_loop()#运转协程loop.run_until_complete(hello_world())#输入:Hello,World!#封锁事情循环loop.close()

在这个示例中,定义了一个异步协程hello_world(),经常使用await关键字暂停口头,以期待asyncio.sleep()成功。而后,创立了一个事情循环并经常使用run_until_complete()方法运转协程。

3.async/await关键字

Python3.5引入了async和await关键字,使协程的成功愈加明晰和繁复。经常使用这些关键字,可以定义异步函数并在其中经常使用await关键字来期待其余协程的口头。

以下是经常使用async和await的示例:

importasyncioasyncdefsay_hello(name):awaitasyncio.sleep(1)print(f"Hello,{name}!")asyncdefmain():awaitasyncio.gather(say_hello("Alice"),say_hello("Bob"))#运转主协程asyncio.run(main())#输入:Hello,Alice!Hello,Bob!

在这个示例中,定义了两个异步函数say_hello(),它们区分经常使用await关键字期待异步I/O操作。而后,经常使用asyncio.gather()来并发运转这两个协程。

协程的运行

协程是一种轻量级的并发编程模型,准许在复线程外口头异步义务,而不须要创立额外的线程或进程。

协程的运行场景包含但不限于以下几个畛域:

留意:协程的运行须要思考到线程安保、共享资源的同步、意外处置等疑问。正确地治理协程,以确保它们不会产生死锁、竞争条件或资源走漏,是协程编程的一个关键应战。

总结

本文引见了Python中协程的成功形式,包含生成器、asyncio库和async/await关键字。协程是一种轻量级的并发编程工具,可用于提高程序的性能和效率。

经过生成器,咱们可以将函数暂停并在须要时复原口头,使其成为可迭代的数据结构,用于成功协程。生成器是协程的最早成功形式之一,可用于处置异步I/O操作。

asyncio库是Python规范库中的异步I/O框架,引入了事情循环,使多个协程可以非阻塞并发运转。它为协程提供了弱小的工具,用于治理异步操作。

Python3.5引入的async和await关键字使协程的成功愈加明晰和繁复。它们使开发者能够以顺序的形式编写异步代码,无需少量回调函数和嵌套。

协程的运行宽泛,适用于网络编程、Web框架、数据库操作等须要高并发性能的畛域。它们提高了程序的并发性,使系统更具吞吐量和照应速度。

经过深化了解和把握协程的成功形式,开发者可以更好地处置并发编程的应战,提高代码的效率和可保养性。


详解Python中的协程,为什么说它的底层是生成器?

协程又称为是微线程,英文名是Coroutine。它和线程一样可以调度,但是不同的是线程的启动和调度需要通过操作系统来处理。并且线程的启动和销毁需要涉及一些操作系统的变量申请和销毁处理,需要的时间比较长。而协程呢,它的调度和销毁都是程序自己来控制的,因此它更加轻量级也更加灵活。

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

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

生成器(generator)

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

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

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

def test():

while n < 10:

val = yield n

print(val = {}(val))

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

我们尝试着运行一下:

# 调用test函数获得一个生成器

print(next(g))

print(next(g))

print(next(g))

得到这么一个结果:

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

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

print(next(g))

print(next(g))

print(next(g))

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

python怎样打开

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

队列调度

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

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

class Scheduler:

def __init__(self):

self._queue = deque()

def new_task(self, task):

self._(task)

def run(self):

while self._queue:

# 每次从队列左侧获取task

task = self._()

# 通过next执行之后放入队列右侧

next(task)

self._(task)

except StopIteration:

sch = Scheduler()

_task(test(5))

_task(test(10))

_task(test(8))

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

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

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

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

import asyncio

def test(k):

print(n = {}(n))

print((test))

print((test(10)))

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

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

loop = _event_loop()

_until_complete(test(10))

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

async,await和future

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

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

async def test(k):

await (0.5)

print(n = {}(n))

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

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

async def test(k):

await (0.5)

print(n = {}(n))

_result(success)

async def log():

result = await future

print(result)

loop = _event_loop()

_until_complete(([

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

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

总结

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

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

Python协程之asyncio

asyncio 是 Python 中的异步IO库,用来编写并发协程,适用于IO阻塞且需要大量并发的场景,例如爬虫、文件读写。

asyncio 在 Python3.4 被引入,经过几个版本的迭代,特性、语法糖均有了不同程度的改进,这也使得不同版本的 Python 在 asyncio 的用法上各不相同,显得有些杂乱,以前使用的时候也是本着能用就行的原则,在写法上走了一些弯路,现在对 Python3.7+ 和 Python3.6 中 asyncio 的用法做一个梳理,以便以后能更好的使用。

协程,又称微线程,它不被操作系统内核所管理,而完全是由程序控制,协程切换花销小,因而有更高的性能。

协程可以比作子程序,不同的是,执行过程中协程可以挂起当前状态,转而执行其他协程,在适当的时候返回来接着执行,协程间的切换不需要涉及任何系统调用或任何阻塞调用,完全由协程调度器进行调度。

Python 中以 asyncio 为依赖,使用 async/await 语法进行协程的创建和使用,如下 async 语法创建一个协程函数:

在协程中除了普通函数的功能外最主要的作用就是:使用 await 语法等待另一个协程结束,这将挂起当前协程,直到另一个协程产生结果再继续执行:

()是 asyncio 包内置的协程函数,这里模拟耗时的IO操作,上面这个协程执行到这一句会挂起当前协程而去执行其他协程,直到sleep结束,当有多个协程任务时,这种切换会让它们的IO操作并行处理。

注意,执行一个协程函数并不会真正的运行它,而是会返回一个协程对象,要使协程真正的运行,需要将它们加入到事件循环中运行,官方建议 asyncio 程序应当有一个主入口协程,用来管理所有其他的协程任务:

在 Python3.7+ 中,运行这个 asyncio 程序只需要一句: (main()),而在 Python3.6 中,需要手动获取事件循环并加入协程任务:

事件循环就是一个循环队列,对其中的协程进行调度执行,当把一个协程加入循环,这个协程创建的其他协程都会自动加入到当前事件循环中。

其实协程对象也不是直接运行,而是被封装成一个个待执行的 Task ,大多数情况下 asyncio 会帮我们进行封装,我们也可以提前自行封装 Task 来获得对协程更多的控制权,注意,封装 Task 需要 当前线程有正在运行的事件循环 ,否则将引 RuntimeError,这也就是官方建议使用主入口协程的原因,如果在主入口协程之外创建任务就需要先手动获取事件循环然后使用底层方法_task() ,而在主入口协程之内是一定有正在运行的循环的。任务创建后便有了状态,可以查看运行情况,查看结果,取消任务等:

_task()是 Python3.7 加入的高层级API,在 Python3.6,需要使用低层级_future()来创建 Future,Future 也是一个管理协程运行状态的对象,与 Task 没有本质上的区别。

通常,一个含有一系列并发协程的程序写法如下(Python3.7+):

并发运行多个协程任务的关键就是(*tasks) ,它接受多个协程任务并将它们加入到事件循环,所有任务都运行完成后会返回结果列表,这里我们也没有手动封装 Task,因为 gather 函数会自动封装。

并发运行还有另一个方法(tasks) ,它们的区别是:

免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。

标签: Python

“Python协程的成功形式 (python怎样打开)” 的相关文章

Python中的LEGB规则 (python怎样打开)

Python中的LEGB规则 (python怎样打开)

Python 中的 LEGB 规则决定了变量和函数的作用域解析顺序。它代表了四个作用域层级: 局部作用域 闭包函数外的函数 全局作用域 内置作用域...

b-b-个入门建议!-Python-技术书籍推荐-附赠-11 (b+b+b等于什么)

b-b-个入门建议!-Python-技术书籍推荐-附赠-11 (b+b+b等于什么)

近年来,Python 持续火爆,越来越多的人开始入门学习 Python。RealPython 作为最受好评的 Python 学习网站,拥有超百万的浏览量,以下是 RealPython 的开发者给...

处置日常义务的终极工具!-Python-文件读写实战 (处置行为是什么意思)

处置日常义务的终极工具!-Python-文件读写实战 (处置行为是什么意思)

/target=_blankclass=infotextkey>Python文件的读写操作时,有很多须要思考的细节,这包含文件关上形式、读取和写入数据的方法、意外处置等。 在本文中,...

Python中的Random模块-摸索随机性的神奇环球 (python编程)

Python中的Random模块-摸索随机性的神奇环球 (python编程)

随机性在计算机编程和数据迷信中表演着至关关键的角色。/target=_blankclass=infotextkey>Python中的random模块提供了丰盛的工具和函数,协助咱们生成随机数...

惰性求值和lambda表达式的强大组合-Python高级技巧 (惰性求值和逻辑短路)

惰性求值和lambda表达式的强大组合-Python高级技巧 (惰性求值和逻辑短路)

Lambda 表达式 在 Python 中,Lambda 表达式是一个匿名函数,它可以在需要函数对象的地方使用。Lambda 表达式的语法如下: lambda arguments: exp...

一份收藏者必备清单-100个精选Python库 (收藏者的心态)

一份收藏者必备清单-100个精选Python库 (收藏者的心态)

/target=_blankclass=infotextkey>Python为啥这么火,这么多人学,就是由于繁难好学,性能弱小,整个社区十分生动,资料很多。而且这言语触及了方方面面,比如智能...

掌握网络世界的无限可能-Python分布式爬虫助力搜索引擎打造 (掌握网络世界的好处)

掌握网络世界的无限可能-Python分布式爬虫助力搜索引擎打造 (掌握网络世界的好处)

主从模式 主从模式是一种简单的分布式爬虫架构,其中一台主机作为控制节点,负责管理所有运行爬虫的从机。 主节点负责向从机分配任务,并接收新生成的任务。从机只需要从主节点接收任务,并把新生...

轻松把握多线程和多进程-Python编程进阶 (多线是什么意思)

轻松把握多线程和多进程-Python编程进阶 (多线是什么意思)

1、简介 咱们将讨论如何应用/target=_blankclass=infotextkey>Python口头多线程和多进程义务。它们提供了在单个进程或多个进程之间口头并发操作的方法。并...