Python 3.4 中的“异步”
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37465816/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
"async with" in Python 3.4
提问by Imran
The Getting Started docs for aiohttp give the following client example:
aiohttp 的入门文档提供了以下客户端示例:
import asyncio
import aiohttp
async def fetch_page(session, url):
with aiohttp.Timeout(10):
async with session.get(url) as response:
assert response.status == 200
return await response.read()
loop = asyncio.get_event_loop()
with aiohttp.ClientSession(loop=loop) as session:
content = loop.run_until_complete(
fetch_page(session, 'http://python.org'))
print(content)
And they give the following note for Python 3.4 users:
他们为 Python 3.4 用户提供了以下说明:
If you are using Python 3.4, please replace await with yield from and async def with a @coroutine decorator.
如果您使用的是 Python 3.4,请将 await 替换为 yield from 并将 async def 替换为 @coroutine 装饰器。
If I follow these instructions I get:
如果我按照这些说明我得到:
import aiohttp
import asyncio
@asyncio.coroutine
def fetch(session, url):
with aiohttp.Timeout(10):
async with session.get(url) as response:
return (yield from response.text())
if __name__ == '__main__':
loop = asyncio.get_event_loop()
with aiohttp.ClientSession(loop=loop) as session:
html = loop.run_until_complete(
fetch(session, 'http://python.org'))
print(html)
However, this will not run, because async with
is not supported in Python 3.4:
但是,这不会运行,因为async with
Python 3.4 不支持:
$ python3 client.py
File "client.py", line 7
async with session.get(url) as response:
^
SyntaxError: invalid syntax
How can I translate the async with
statement to work with Python 3.4?
如何翻译该async with
语句以使用 Python 3.4?
采纳答案by Martijn Pieters
Just don't use the result of session.get()
as a context manager; use it as a coroutine directly instead. The request context manager that session.get()
produces would normally releasethe requeston exit, but so does using response.text()
, so you could ignore that here:
只是不要将结果session.get()
用作上下文管理器;直接将其用作协程。产生的请求上下文管理器session.get()
通常会在退出时释放请求,但使用response.text()
也是如此,因此您可以在此处忽略它:
@asyncio.coroutine
def fetch(session, url):
with aiohttp.Timeout(10):
response = yield from session.get(url)
return (yield from response.text())
The request wrapper returned here doesn't have the required asynchronous methods (__aenter__
and __aexit__
), they omitted entirely when not using Python 3.5 (see the relevant source code).
此处返回的请求包装器没有所需的异步方法(__aenter__
和__aexit__
),它们在不使用 Python 3.5 时完全省略(请参阅相关源代码)。
If you have more statements between the session.get()
call and accessing the response.text()
awaitable, you probably want to use a try:..finally:
anyway to release the connection; the Python 3.5 release context manager also closesthe response if an exception occurred. Because a yield from response.release()
is needed here, this can't be encapsulated in a context manager before Python 3.4:
如果在session.get()
调用和访问response.text()
awaitable之间有更多语句,您可能希望使用 atry:..finally:
来释放连接;如果发生异常,Python 3.5 发布上下文管理器也会关闭响应。因为yield from response.release()
这里需要a ,所以在Python 3.4之前不能封装在上下文管理器中:
import sys
@asyncio.coroutine
def fetch(session, url):
with aiohttp.Timeout(10):
response = yield from session.get(url)
try:
# other statements
return (yield from response.text())
finally:
if sys.exc_info()[0] is not None:
# on exceptions, close the connection altogether
response.close()
else:
yield from response.release()
回答by Mikhail Gerasimov
aiohttp
's examplesimplemented using 3.4 syntax. Based on json client exampleyour function would be:
aiohttp
的示例使用 3.4 语法实现。基于json 客户端示例,您的功能将是:
@asyncio.coroutine
def fetch(session, url):
with aiohttp.Timeout(10):
resp = yield from session.get(url)
try:
return (yield from resp.text())
finally:
yield from resp.release()
Upd:
更新:
Note that Martijn's solution would work for simple cases, but may lead to unwanted behavior in specific cases:
请注意,Martijn 的解决方案适用于简单情况,但在特定情况下可能会导致不良行为:
@asyncio.coroutine
def fetch(session, url):
with aiohttp.Timeout(5):
response = yield from session.get(url)
# Any actions that may lead to error:
1/0
return (yield from response.text())
# exception + warning "Unclosed response"
Besides exception you'll get also warning "Unclosed response". This may lead to connections leak in complex app. You will avoid this problem if you'll manually call resp.release()
/resp.close()
:
除了例外,您还会收到警告“未关闭的响应”。这可能会导致复杂应用程序中的连接泄漏。如果您手动调用resp.release()
/ ,您将避免此问题resp.close()
:
@asyncio.coroutine
def fetch(session, url):
with aiohttp.Timeout(5):
resp = yield from session.get(url)
try:
# Any actions that may lead to error:
1/0
return (yield from resp.text())
except Exception as e:
# .close() on exception.
resp.close()
raise e
finally:
# .release() otherwise to return connection into free connection pool.
# It's ok to release closed response:
# https://github.com/KeepSafe/aiohttp/blob/master/aiohttp/client_reqrep.py#L664
yield from resp.release()
# exception only
I think it's better to follow official examples (and __aexit__
implementation) and call resp.release()
/resp.close()
explicitly.
我认为最好遵循官方示例(和__aexit__
实现)并明确调用resp.release()
/ resp.close()
。