前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【翻译】Python async/await Tutorial

【翻译】Python async/await Tutorial

作者头像
小小科
发布2018-05-03 15:42:53
1.4K0
发布2018-05-03 15:42:53
举报
文章被收录于专栏:北京马哥教育北京马哥教育

原文链接: http://stackabuse.com/python-async-await-tutorial/

过去几年,异步编程方式被越来越多的程序员使用, 当然这是有原因的。 尽管异步编程比顺序编程更难, 但是它也更高效。

在顺序编程中, 发起一个HTTP请求需要阻塞以等待他的返回结果, 使用异步编程你可以发起这个HTTP请求, 然后在等待结果返回的同时做一些其他的事情,等待结果的协程会被放在一个队列里面。 为了保证逻辑的正确性, 这可能会需要考虑的更多, 但是这也使我们用更少的资源处理更多的事情。 Python中的异步语法和调用并不难。 和Javascript的异步编程比起来可能有点难, 不过也还好。

异步的处理方式可能解释了为什么Node.js在服务器端这么流行。 我们的代码依赖于外部的一些资源, 尤其是IO密集型的应用, 比如网站可能需要从数据库调用中向一个REST接口POST数据。 一旦我们请求一些外部资源, 我们的代码就要阻塞, 而不能处理别的逻辑。

利用异步编程, 我们可以在等待其他资源返回的时候, 做一些其他的事情。

Coroutines

在python中,异步函数被称作协程: 使用async关键字 或者利用@asyncio.coroutine装饰器。 下面的两种形式是等效的:

代码语言:javascript
复制
代码语言:javascript
复制
import asyncio

async def ping_server(ip):  
    pass


@asyncio.coroutinedef load_file(path):  
    pass
代码语言:javascript
复制

上面的函数调用的时候返回的是一个协程的对象。 如果你熟悉javascript, 你可以认为这个返回对象就像javascript里面的Promise。 现在调用这两个函数, 是不能执行的, 仅仅返回的是一个协程对象, 这个对象可以被用来在后面的event loop中使用。

如果你想知道一个函数是不是协程, asyncio提供的asyncio.iscoroutine(obj)函数可以帮助你。

Yield from

有几种调用协程的方式,其中一种是使用yield from方法。 yield from在Python3.3中被引进, 在Python3.5的async/await(我们后面会提到) 得到进一步的扩展。 yield from表达式可以用如下的方式使用:

代码语言:javascript
复制
import asyncio@asyncio.coroutinedef get_json(client, url):  
    file_content = yield from load_file('/Users/scott/data.txt')

如上, yield from在有@asyncio.coroutine装饰器的函数中使用的示例。 如果你在函数外面使用yield from, 你会得到下面的错误:

代码语言:javascript
复制
File "main.py", line 1
    file_content = yield from load_file('/Users/scott/data.txt')
                  ^SyntaxError: 'yield' outside function  

必须在函数中使用yield from, 典型的用法是在有@asyncio.coroutine装饰器的函数种使用。

Async/await

更新、更方便的语法是使用async/await关键字。async关键字是在Python3.5引入的, 被用来修饰一个函数, 让其成为协程, 和@asyncio.coroutine功能类似。 使用如下:

代码语言:javascript
复制
async def ping_server(ip):  
    # ping code here...

调用这个函数, 使用await, 而不是yield from, 不过方式差不多:

代码语言:javascript
复制
async def ping_local():  
    return await ping_server('192.168.1.1')

你不能在一个协程外面使用await关键字, 否则会得到语法错误。 就像yield from不能在函数外面使用一样。

Python3.5中, 上面两种协程声明的方式都支持, 但是首选async/await方式。

Running the event loop

上面描述的协程例子都不会正常的运行, 如果要运行, 需要用到event loop.。event loop是协程执行的控制点, 如果你希望执行协程, 就需要用到它们。

event loop提供了如下的特性:

  • 注册、执行、取消延时调用(异步函数)
  • 创建用于通信的client和server协议(工具)
  • 创建和别的程序通信的子进程和协议(工具)
  • 把函数调用送入线程池中

有一些配置和event loop的类型你可以使用, 但是如果你想去执行一个函数, 可以使用下面的配置, 而且在大多数场景中这样就够了:

代码语言:javascript
复制
import asyncio

async def speak_async():  
    print('OMG asynchronicity!')

loop = asyncio.get_event_loop()  
loop.run_until_complete(speak_async())  
loop.close()  

最后三行是重点。 asyncio启动默认的event loop(asyncio.get_event_loop()), 调度并执行异步任务, 关闭event loop。

loop.run_until_complete()这个函数是阻塞执行的, 直到所有的异步函数执行完毕。 因为我们的程序是单线程运行的, 所以, 它没办法调度到别的线程执行。

你可能会认为这不是很有用, 因为我们的程序阻塞在event loop上(就像IO调用), 但是想象一下这样: 我们可以把我们的逻辑封装在异步函数中, 这样你就能同时执行很多的异步请求了, 比如在一个web服务器中。

你可以把event loop放在一个单独的线程中, 让它执行IO密集型的请求, 而主线程可以继续处理程序逻辑或者UI渲染。

An example

OK, 让我看一个稍微长一点的例子, 这个例子是可以实际运行的。 例子是一个简单的从Reddit的/r/python, /r/programming, and /r/compsci页面异步获取JSON数据, 解析, 打印出这些页面发表的文章。

get_json()方法是被get_reddit_top()调用的, get_reddit_top()发起了一个HTTP GET请求到Reddit。 当调用被await修饰, event loop就会继续在等待请求返回的时候处理其他的协程。 一旦请求返回, JSON数据会被返回get_reddit_top(), 然后解析, 打印。

代码语言:javascript
复制
import signal  import sys  import asyncio  import aiohttp  import json

loop = asyncio.get_event_loop()  
client = aiohttp.ClientSession(loop=loop)

async def get_json(client, url):  
    async with client.get(url) as response:        assert response.status == 200
        return await response.read()

async def get_reddit_top(subreddit, client):  
    data1 = await get_json(client, 'https://www.reddit.com/r/' + subreddit + '/top.json?sort=top&t=day&limit=5')

    j = json.loads(data1.decode('utf-8'))    for i in j['data']['children']:
        score = i['data']['score']
        title = i['data']['title']
        link = i['data']['url']
        print(str(score) + ': ' + title + ' (' + link + ')')

    print('DONE:', subreddit + '\n')def signal_handler(signal, frame):  
    loop.stop()
    client.close()
    sys.exit(0)

signal.signal(signal.SIGINT, signal_handler)

asyncio.ensure_future(get_reddit_top('python', client))  
asyncio.ensure_future(get_reddit_top('programming', client))  
asyncio.ensure_future(get_reddit_top('compsci', client))  
loop.run_forever()  

这个程序和上面展示的例子有一点不同。 我们使用asyncio.ensure_future()让event loop处理多个协程, 然后让event loop一直执行, 直到处理了所有的请求。

为了执行这个程序, 需要安装aiohttp, 你可以用pip来安装:

代码语言:javascript
复制
pip install aiohttp

要保证这个程序运行在python3.5以后的版本, 输出的结果如下:

代码语言:javascript
复制
$ python main.py
46: Python async/await Tutorial (http://stackabuse.com/python-async-await-tutorial/)  
16: Using game theory (and Python) to explain the dilemma of exchanging gifts. Turns out: giving a gift probably feels better than receiving one... (http://vknight.org/unpeudemath/code/2015/12/15/The-Prisoners-Dilemma-of-Christmas-Gifts/)  56: Which version of Python do you use? (This is a poll to compare the popularity of Python 2 vs. Python 3) (http://strawpoll.me/6299023)  
DONE: python71: The Semantics of Version Control - Wouter Swierstra (http://www.staff.science.uu.nl/~swier004/Talks/vc-semantics-15.pdf)  25: Favorite non-textbook CS books (https://www.reddit.com/r/compsci/comments/3xag9e/favorite_nontextbook_cs_books/)  13: CompSci Weekend SuperThread (December 18, 2015) (https://www.reddit.com/r/compsci/comments/3xacch/compsci_weekend_superthread_december_18_2015/)  
DONE: compsci1752: 684.8 TB of data is up for grabs due to publicly exposed MongoDB databases (https://blog.shodan.io/its-still-the-data-stupid/)  773: Instagram's Million Dollar Bug? (http://exfiltrated.com/research-Instagram-RCE.php)  
387: Amazingly simple explanation of Diffie-Hellman. His channel has tons of amazing videos and only a few views :( thought I would share! (https://www.youtube.com/watch?v=Afyqwc96M1Y)  
DONE: programming  

如果你多运行几次这个程序, 得到的输出结果是不一样的。 这是因为我们调用的协程的同时, 允许其他的HTTP请求执行。 结果最先返回的请求最先打印出来。

总结

尽管Python内置的异步函数使用起来没有Javascript中的那么简便, 不过, 这不意味着它不能使应用更有趣和高效。 花费30分钟去学习异步相关的知识, 你就能更好的把它应用在你的项目中。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 马哥Linux运维 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Coroutines
  • Yield from
  • Async/await
  • Running the event loop
  • An example
  • 总结
相关产品与服务
云数据库 MongoDB
腾讯云数据库 MongoDB(TencentDB for MongoDB)是腾讯云基于全球广受欢迎的 MongoDB 打造的高性能 NoSQL 数据库,100%完全兼容 MongoDB 协议,支持跨文档事务,提供稳定丰富的监控管理,弹性可扩展、自动容灾,适用于文档型数据库场景,您无需自建灾备体系及控制管理系统。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档