Python async/await 手册
Pythonasync/await手册,在过去几年内,异步编程由于某些好的原因得到了充分的重视。虽然它比线性编程难一点,但是效率相对来说也是更高。
比如,利用Python的异步协程(asynccoroutine),在提交HTTP请求后,就没必要等待请求完成再进一步操作,而是可以一边等着请求完成,一边做着其他工作。这可能在逻辑上需要多些思考来保证程序正确运行,但是好处是可以利用更少的资源做更多的事。
即便逻辑上需要多些思考,但实际上在Python语言中,异步编程的语法和执行并不难。跟Javascript不一样,现在Python的异步协程已经执行得相当好了。
对于服务端编程,异步性似乎是Node.js流行的一大原因。我们写的很多代码,特别是那些诸如网站之类的高I/O应用,都依赖于外部资源。这可以是任何资源,包括从远程数据库调用到POST一个REST请求。一旦你请求这些资源的任一一个,你的代码在等待资源响应时便无事可做(译者注:如果没有异步编程的话)。
有了异步编程,在等待这些资源响应的过程中,你的代码便可以去处理其他的任务。
协程(Coroutines)
在Python中,异步函数通常被称作协程,创建一个协程仅仅只需使用async关键字,或者使用@asyncio.coroutine装饰器。下面的任一代码,都可以作为协程工作,形式上也是等同的:
importasyncio
asyncdefping_server(ip):
pass
@asyncio.coroutine
defload_file(path):
pass
上面这俩特殊的函数,在调用时会返回协程对象。熟悉JavaScript中Promise的同学,可以把这个返回对象当作跟Promise差不多。调用他们中的任意一个,实际上并未立即运行,而是返回一个协程对象,然后将其传递到Eventloop中,之后再执行。
如果要判断一个函数是不是协程,asyncio提供了asyncio.iscoroutinefunction(func)方法,如果要判断一个函数返回的是不是协程对象,则可以使用asyncio.iscoroutine(obj)。
Yieldfrom
调用协程的方式有有很多,yieldfrom就是其中的一种。这种方式在Python3.3中被引入,在Python3.5中以async/await的形式进行了优化。yieldfrom表达式的使用方式如下:
importasyncio
@asyncio.coroutine
defget_jason(client,url):
file_content=yieldfromload_file('/Usrs/scott/data.txt')
正如所看到的,yieldfrom被使用在用@asyncio.coroutine装饰的函数内,如果想把yieldfrom在这个函数外使用,将会抛出如下语法错误:
File"main.py",line1
file_content=yieldfromload_file('/Users/scott/data.txt')
^
SyntaxError:'yield'outsidefunction
为了避免语法错误,yieldfrom必须在调用函数的内部使用(这个调用函数通常被装饰为协程)。
Async/await
较新的语法是使用async/await关键字。async从Python3.5开始被引进,跟@asyncio.coroutine装饰器一样,用来声明一个函数是一个协程。只要把它放在函数定义之前,就可以应用到函数上,使用方式如下:
asyncdefping_server(ip):
#pingcodehere...
实际调用这个函数时,使用await而不用yieldfrom,当然,使用方式依然差不多:
asyncdefping_local(ip):
returnawaitping_server('192.168.1.1')
再强调一遍,跟yieldfrom一样,不能在函数外部使用await,否则会抛出语法错误。(译者注:async用来声明一个函数是协程,然后使用await调用这个协程,await必须在函数内部,这个函数通常也被声明为另一个协程)
Python3.5对这两种调用协程的方法都提供了支持,但是推荐async/await作为首选。
EventLoop
如果你还不知道如何开始和操作一个Eventloop,那么上诉有关协程所说的都起不了多大作用。Eventloop在执行异步函数时非常重要,重要到只要执行协程,基本上就得利用Eventloop。
Eventloop提供了相当多的功能:
注册,执行和取消延迟调用(异步函数)
创建客户端与服务端传输用于通信
创建子程序和通道跟其他的程序进行通信
指定函数的调用到线程池
Eventloop有相当多的配置和类型可供使用,但大部分程序只需要如下方式预定函数即可:
importasyncio
asyncdefspeak_async():
print('OMGasynchronicity!')
loop=asyncio.get_event_loop()
loop.run_until_complete(speak_async())
loop.close()
有意思的是代码中的最后三行,首先获取默认的Eventloop(asyncio.get_event_loop()),然后预定和运行异步任务,并在完成后结束循环。
loop.run_until_complete()函数实际上是阻塞性的,也就是在所有异步方法完成之前,它是不会返回的。但因为我们只在一个线程中运行这段代码,它没法再进一步扩展,即使循环仍在运行。
可能你现在还没觉得这有多大的用处,因为我们通过调用其他IO来结束Eventloop中的阻塞(译者注:也就是在阻塞时进行其他IO),但是想象一下,如果在网页服务器上,把整个程序都封装在异步函数内,到时就可以同时运行多个异步请求了。
也可以将Eventloop的线程中断,利用它去处理所有耗时较长的IO请求,而主线程处理程序逻辑或者用户界面。
一个案例
让我们实际操作一个稍大的案例。下面这段代码就是一个非常漂亮的异步程序,它先从Reddit抓取JSON数据,解析它,然后打印出当天来自/r/python,/r/programming和/r/compsci的置顶帖。
所示的第一个方法get_json(),由get_reddit_top()调用,然后只创建一个GET请求到适当的网址。当这个方法和await一起调用后,Eventloop便能够继续为其他的协程服务,同时等待HTTP响应达到。一旦响应完成,JSON数据就返回到get_reddit_top(),得到解析并打印出来。
asyncdefget_reddit_top(subreddit,client):
data1=awaitget_json(client,'https://www.reddit.com/r/'+subreddit+'/top.json?sort=top&t=day&limit=5')
j=json.loads(data1.decode('utf-8'))
foriinj['data']['children']:
score=i['data']['score']
title=i['data']['title']
link=i['data']['url']
print(str(score)+':'+title+'('+link+')')
print('DONE:',subreddit+'\n')
defsignal_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()
这跟我们之前展示出来的代码略有不同。为了达到在Eventloop中运行多重协程的目的,我们使用asyncio.ensure_future(),然后运行无限循环以处理一切。
为了保证运行成功,你必须先安装aiohttp,可以使用PIP安装:
pipinstallaiohttp
现在,只要保证你运行的Python3.5或者更高版本,你就可以获得如下输出:
$pythonmain.py
46:Pythonasync/awaitTutorial(http://stackabuse.com/python-async-await-tutorial/)
16:Usinggametheory(andPython)toexplainthedilemmaofexchanginggifts.Turnsout:givingagiftprobablyfeelsbetterthanreceiving
56:WhichversionofPythondoyouuse?(ThisisapolltocomparethepopularityofPython2vs.Python3)(http://strawpoll.me/6299023
DONE:python
71:TheSemanticsofVersionControl-WouterSwierstra(http://www.staff.science.uu.nl/~swier004/Talks/vc-semantics-15.pdf)
25:Favoritenon-textbookCSbooks(https://www.reddit.com/r/compsci/comments/3xag9e/favorite_nontextbook_cs_books/)
13:CompSciWeekendSuperThread(December18,2015)(https://www.reddit.com/r/compsci/comments/3xacch/compsci_weekend_superthread_december_18
DONE:compsci
1752:684.8TBofdataisupforgrabsduetopubliclyexposedMongoDBdatabases(https://blog.shodan.io/its-still-the-data-stupid/)
773:Instagram'sMillionDollarBug?(http://exfiltrated.com/research-Instagram-RCE.php)
387:AmazinglysimpleexplanationofDiffie-Hellman.Hischannelhastonsofamazingvideosandonlyafewviews:(thoughtIwouldshare!(h
DONE:programming
注意,如果多次运行这段代码,打印出来的subreddit数据在顺序上会有些许变化。这是因为每当我们调用一次代码都会释放对线程的控制,容许线程去处理另一个HTTP调用。这将导致谁先获得响应,谁就先打印出来。
结论
即使Python内置的异步操作没有Javascript那么顺畅,但这并不意味着就不能用它来把应用变得更有趣、更有效率。只要花半个小时的时间去了解它的来龙去脉,你就会感觉把异步操作应用到你的程序中将会是多美好的一件事。
Python培训、Python培训班、Python培训机构,就选光环大数据!
还不够过瘾?想学习更多?点击 http://hadoop.aura.cn/python/ 进行Python学习!
大数据培训、人工智能培训、Python培训、大数据培训机构、大数据培训班、数据分析培训、大数据可视化培训,就选光环大数据!光环大数据,聘请专业的大数据领域知名讲师,确保教学的整体质量与教学水准。讲师团及时掌握时代潮流技术,将前沿技能融入教学中,确保学生所学知识顺应时代所需。通过深入浅出、通俗易懂的教学方式,指导学生更快的掌握技能知识,成就上万个高薪就业学子。 更多问题咨询,欢迎点击------>>>>在线客服!