Python 根据参数使函数异步

Python 根据参数使函数异步,python,python-asyncio,Python,Python Asyncio,我有一个函数,它发出HTTP请求,然后返回响应。我希望这个函数能够根据参数在阻塞或非阻塞模式下运行。这在Python中是可能的吗?我想象的伪代码是这样的: def\u异步(异步): 如果是异步的: #我们假设有一个事件循环正在运行,我们可以等待 返回等待发送\u异步\u http\u请求() 其他: #执行正常的同步调用 返回发送\u http\u请求() 这会抛出一个SyntaxError,我想找到一种方法来重新表述它,这样它就会抛出RuntimeError:如果asynchronous=T

我有一个函数,它发出HTTP请求,然后返回响应。我希望这个函数能够根据参数在阻塞或非阻塞模式下运行。这在Python中是可能的吗?我想象的伪代码是这样的:

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
,它应该可以按照您的需要工作。@zixuan
SyntaxError在异步函数
外部:“wait”之所以中断,是因为
get\github\u events\u id
是错误的,而不是因为
get
是错误的。更改
get
并不能解决这一问题。因为包含了(几乎)可运行的示例代码而备受赞誉。我现在发布了一个回复,它太神奇了,不能称之为“正确的Python”,但它确实使用公共API解决了这个问题。这太棒了。魔法,是的,但绝对解决了问题!谢谢