摘要:

  1. Python3.5的新特性,原文地址
  2. asyncawait等新语法来进行协程编程

原文链接

What’s New In Python 3.5

PEP 492 - Coroutines with async and await syntax

通过添加awaitabel对象,协程函数,异步迭代器和异步内容管理器,PEP492极大地提升了在Python中进行协程编程的能力。

通过async func语法可以声明一个协程函数,如下所示:

1
2
async def coro():
    return 'span'

在一个协程函数内部,新的await表达式可以用来暂停协程的执行,直到新的结果可用,任何对象都可以被await化,只要这个对象通过定义__await__函数实现了awaitabel协议

PEP492 也添加了async for语句来方便进行异步迭代器的迭代。

示例: 基本的Http客户端

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import asyncio

async def http_get(domain):
    reader, writer = await asyncio.open_connection(domain, 80)

    writer.write(b'\r\n'.join([
        b'GET / HTTP/1.1',
        b'Host: %b' % domain.encode('latin-1'),
        b'Connection: close',
        b'', b''
    ]))

    async for line in reader:
        print('>>>', line)

    writer.close()

loop = asyncio.get_event_loop()
try:
    loop.run_until_complete(http_get('baidu.com'))
finally:
    loop.close()

在上面的程序中,首先定义了一个事件循环和一个协程函数,协程函数跑在事件循环上,在协程函数内部,异步地去请求服务端的连接,连接成功建立以后,再向服务端发出Http请求,返回的Http相应被存储在reader这个异步迭代器中,然后再来通过async for来遍历出这个异步迭代器的所有结果。

程序的输入如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
>>> b'HTTP/1.1 200 OK\r\n'
>>> b'Date: Sun, 08 May 2016 07:33:29 GMT\r\n'
>>> b'Server: Apache\r\n'
>>> b'Last-Modified: Tue, 12 Jan 2010 13:48:00 GMT\r\n'
>>> b'ETag: "51-47cf7e6ee8400"\r\n'
>>> b'Accept-Ranges: bytes\r\n'
>>> b'Content-Length: 81\r\n'
>>> b'Cache-Control: max-age=86400\r\n'
>>> b'Expires: Mon, 09 May 2016 07:33:29 GMT\r\n'
>>> b'Connection: Close\r\n'
>>> b'Content-Type: text/html\r\n'
>>> b'\r\n'
>>> b'<html>\n'
>>> b'<meta http-equiv="refresh" content="0;url=http://www.baidu.com/">\n'
>>> b'</meta>html>\n'

示例: 上下文管理器

除了异步迭代器之外,还有新的语法支持异步的上下文管理器(关于上下文管理器可以参考我的这篇文章Python的with语句),示例程序如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
import asyncio

async def coro(name, lock):
    print('coro {}: waiting for lock'.format(name))
    async with lock:
        print('coro {}: holding the lock'.format(name))
        await asyncio.sleep(1)
        print('coro {}: releasing the lock'.format(name))

loop = asyncio.get_event_loop()
lock = asyncio.Lock()
coros = asyncio.gather(coro(1, lock), coro(2, lock))
try:
    loop.run_until_complete(coros)
finally:
    loop.close()

在上面的程序中,通过asyncio.gather函数将两个协程函数定义的协程对象生成一个future对象,然后通过一个事件循环去运行这个future对象。 在协程函数内部,主要就是异步的获取锁,并且和with语句结合,可以在获取以后自动释放锁。

程序的运行结果如下:

1
2
3
4
5
6
coro 1: waiting for lock
coro 1: holding the lock
coro 2: waiting for lock
coro 1: releasing the lock
coro 2: holding the lock
coro 2: releasing the lock

需要注意的是async withasync for必须运行在async def定义的协程函数内部。 协程函数必须运行在一个兼容的事件循环之上,例如asyncio loop