专栏名称: Python程序员
最专业的Python社区,有每日推送,免费电子书,真人辅导,资源下载,各类工具。我已委托“维权骑士”(rightknights.com)为我的文章进行维权行动
目录
相关文章推荐
Python爱好者社区  ·  刷新三观,老板说开发超10个Bug就开除,之 ... ·  3 天前  
Python中文社区  ·  出海赚美元!程序员摆脱35岁危机的出路? ·  4 天前  
Python爱好者社区  ·  AI 之王 GPT-6 猎户座 来了!大模型杀疯了 ·  4 天前  
Python爱好者社区  ·  机房锂电池火灾致阿里云服务瘫痪,超 30 ... ·  4 天前  
Python爱好者社区  ·  刷新三观,深圳某公司重新定义八小时工作制 ·  6 天前  
51好读  ›  专栏  ›  Python程序员

Python async/await教程

Python程序员  · 公众号  · Python  · 2017-05-21 10:31

正文

Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发。

异步编程在过去的几年里获得了大量的支持,这是有原因的。虽然它比传统的流式编程更难,但是也更高效。

例如,使用Python异步协同程序,在继续执行前不需要等待HTTP请求完成,你可以提交请求,一边做其他队列中等待的工作一边等待HTTP请求完成。这可能需要思考更多来获得正确的逻辑,但是你可以以更少的资源处理更多的工作。

即便如此,异步函数的语法和执行在Python这样的语言中实际上并不困难。

异步性似乎是node.js在服务器端编程如此受欢迎的最大原因。我们写的大部分代码取决于外部资源,尤其是在像网站一样的重IO应用。依赖外部资源可以是任何东西,从远程数据库调用到给REST服务发送POST请求。一旦你请求这些资源,你的代码就开始等待不做其他事情。

使用异步编程,在等待其他资源响应时,允许代码处理其他任务。


协程

Python中的异步函数通常被称为“协程”,它仅仅是一个采用异步关键字或是用@asyncio.coroutine装饰的函数。两个函数中的任何一个都将作为协同程序工作并且类型是等效的:

这些特殊函数当被调用时返回协同对象时。如果你熟悉JavaScript Promises对象的话,你可以把这个返回对象看作Promises对象。调用这些函数的任何一个实际上并没有运行他们,而是返回一个协同程序对象,然后可以传递到事件循环执行。

如果你需要决定一个函数是否是协同程序,asyncio提供了asyncio.iscoroutinefunction(func) 方法恰好可以为你完成这个,或者如果你需要确定,从一个函数返回的对象是否是协同程序对象,你可以使用asyncio iscoroutine(obj)方法代替。


Yield from

有几个途径,可以实际调用协同程序,其中的一个是 yield from 方法。这个方法在Python3.3中被引入。在Python 3.5中有了更进一步地改进为异步/等待的形式(我们后面会谈到)。

yield from表达式可以像下面这样使用:

正如你所看到的,yield from被一个装饰着@asyncio.coroutine的函数使用。如果你尝试从函数外使用yield from,那么你会得到这样的Python错误:

为了使用这种语法,它必须在另一个函数里(通常用协同程序装饰))。


async/await

更新的和更清洁的语法是使用async/await关键字,async在Python 3.5中引入,用于作为一个协同程序声明一个函数,就像@asyncio.coroutine装饰器所做的,通过把它放到函数定义前使它应用于函数:

实际调用这个函数,我们使用await,取代yield from,但以同样的方式:

就像yield from一样,你不能在另一个协同程序外使用这个,否则会得到语法错误。

在Python 3.5中,两种调用协同程序的方式都是支持的,但是async/await方式是主要的语法。


运行事件循环

如果你不知道如何启动和运行事件循环的话,上面描述的协同程序的东西都不会起作用,事件循环是异步函数执行的中心点,所以当你想实际执行协同程序,这是您将使用的。

事件循环提供了相当多的特性:

注册,执行和取消延迟调用(异步函数)

和另一个程序创建子进程和传输进行通信

函数调用委托给一个线程池

虽然实际上有相当多的配置和事件循环类型可以使用,大部分的程序编写将只需要这样来安排函数:

最后三行是我们感兴趣的,它以得到默认的事件循环开始 (asyncio.get_event_loop()),调度和运行异步任务,然后在循环完成时关闭循环。

loop.run_until_complete()函数实际上是阻塞的,因此在完成所有异步方法之前,它不会返回值。因为我们是在单线程上运行,所以不可能在循环进行过程中做进一步处理。

现在,你可能认为这不是很有用,因为我们最终阻塞事件循环(而不是IO调用),但是想象一下在一个异步函数包装你的整个项目,这将允许您同时运行很多异步请求,就像在一个web服务器上。

您甚至可以将事件循环中断到自己的线程,让它处理所有的IO请求,而主线程处理程序逻辑或UI。


一个例子

好,我们来看一个我们可以运行的更大的例子。下面的代码是一个非常简单的异步程序,它从Reddit中获取JSON,解析JSON,并打印出当天的头条文章,从/ r / python,/ r /编程到/ r / compsci。

其中的第一个函数get_json()get_reddit_top()调用,只创建一个HTTP GET请求到相应的Reddit URL。当await被调用时,事件循环就可以继续运行并在其他的coroutines上进行服务,等待HTTP响应返回。一旦它完成,JSON将返回到get_reddit_top(),得到解析,并被打印出来。

这与我们之前展示的示例代码有一点不同。为了获得在事件循环上运行的多个coroutines,我们使用asyncio.ensure_future(),然后运行循环来处理所有的事情。

为了运行这个,你需要先安装aiohttp,你可以用PIP来做:

现在只需要确保使用Python 3.5或更高版本运行,您就应该得到这样的输出:

请注意,如果您运行这个程序几次,那么subreddit数据打印出来的顺序会发生变化。这是因为我们每一个调用都会释放线程的控制权,允许另一个HTTP调用进程。第一个返回的第一个打印出来。


结论

尽管Python的内置异步功能不像JavaScript那样平滑,但这并不意味着您不能将其用于有趣和高效的应用程序。只需花30分钟来了解它的来龙去脉,你就能更好地理解如何将其集成到自己的应用程序中。

您如何看待Python的async/await?你过去用过它吗?在评论中告诉我们吧!


英文原文:http://www.stackabuse.com/python-async-await-tutorial/
译者:星圻