Python 根据参数使函数异步
我有一个函数,它发出HTTP请求,然后返回响应。我希望这个函数能够根据参数在阻塞或非阻塞模式下运行。这在Python中是可能的吗?我想象的伪代码是这样的:Python 根据参数使函数异步,python,python-asyncio,Python,Python Asyncio,我有一个函数,它发出HTTP请求,然后返回响应。我希望这个函数能够根据参数在阻塞或非阻塞模式下运行。这在Python中是可能的吗?我想象的伪代码是这样的: def\u异步(异步): 如果是异步的: #我们假设有一个事件循环正在运行,我们可以等待 返回等待发送\u异步\u http\u请求() 其他: #执行正常的同步调用 返回发送\u http\u请求() 这会抛出一个SyntaxError,我想找到一种方法来重新表述它,这样它就会抛出RuntimeError:如果asynchronous=T
def\u异步(异步):
如果是异步的:
#我们假设有一个事件循环正在运行,我们可以等待
返回等待发送\u异步\u http\u请求()
其他:
#执行正常的同步调用
返回发送\u http\u请求()
这会抛出一个SyntaxError
,我想找到一种方法来重新表述它,这样它就会抛出RuntimeError:如果asynchronous=True
但没有运行事件循环,则在运行时不会运行事件循环
有两条评论说我只需要从maybe_async
中删除wait
关键字,但是我认为如果我们想对响应进行后期处理,那么这是不相关的。这里有一个更具体的用例:假设我想向最终用户提供一个函数,该函数从GitHub API收集所有事件ID,并根据用户输入以阻塞或非阻塞模式执行。以下是我想做的:
导入aiohttp
导入请求
异步定义获取异步(url):
与aiohttp.ClientSession()作为会话异步:
以session.get(url)作为响应的异步:
return wait response.json()
def get_同步(url):
return requests.get(url).json()
def get(url,异步):
如果是异步的:
return get_async(url)#这是建议的编辑方式
#我删除了wait关键字
其他:
返回获取同步(url)
def get_github_事件_ID(异步=假):
url='1〕https://api.github.com/events'
body=get(url,异步)
返回[主体中事件的事件['id']
但显然,运行get\u github\u events\u id(True)
会引发TypeError:“coroutine”对象不可编辑
我的问题是:除了复制所有允许在同步和异步之间进行选择的函数之外,还有其他代码设计吗?问题是,在asyncio下,像get\u github\u events\u id
这样的函数本身必须是异步的,或者返回通过调用(但不等待)异步函数获得的对象。这允许他们在等待结果到达时暂停执行
您可以为每个函数创建两个版本,一个等待,另一个运行代码,但这将导致大量代码重复。有一个更好的方法,但它需要一点魔力
首先,在内部,代码必须始终使用async,因为这是在async情况下传播挂起的唯一方法。但是在sync的情况下,它可以将sync调用返回的对象包装成可以等待的东西(因为协同路由只返回sync调用的结果),我们可以在顶层等待。我们称之为“等待”。在异步情况下的顶层,我们可以将协同路由对象返回给调用方,调用方将等待它,而在同步情况下,我们可以驱动协同路由完成,我们将恰当地称之为“drive”
下面是get\u github\u events\u id
的内容:
def get_github_events_ids(asynchronous=False):
coro = _get_github_events_ids_impl(asynchronous)
if asynchronous:
return coro # let the caller await
else:
return drive(coro) # get sync result from "coroutine"
实现看起来总是异步的:
async def _get_github_events_ids_impl(asynchronous):
url = 'https://api.github.com/events'
body = await awaitify(get(url, asynchronous))
return [event['id'] for event in body]
# "get", "get_sync", and "get_async" remain exactly as
# in the question
现在我们只需要定义awaitify
和drive
魔术功能:
def awaitify(obj):
if isinstance(obj, types.CoroutineType):
return obj # nothing to do, we're already async
# return an async shim that will just return the object
async def _identity():
return obj
return _identity()
def drive(coro):
# coro is not really async, so we don't need an event loop or
# anything, we just drive the coroutine object to completion.
# Don't try this at home!
while True:
try:
coro.send(None)
except StopIteration as done:
return done.value
要测试它,只需以两种方式运行它:
if __name__ == '__main__':
# test sync
print(get_github_events_ids(False))
# test async
print(asyncio.run(get_github_events_ids(True)))
它抛出了什么特定的SyntaxError?在这个简单的示例中,您可以删除wait
,它应该可以按照您的需要工作。@zixuanSyntaxError在异步函数
外部:“wait”之所以中断,是因为get\github\u events\u id
是错误的,而不是因为get
是错误的。更改get
并不能解决这一问题。因为包含了(几乎)可运行的示例代码而备受赞誉。我现在发布了一个回复,它太神奇了,不能称之为“正确的Python”,但它确实使用公共API解决了这个问题。这太棒了。魔法,是的,但绝对解决了问题!谢谢