将代码同步到异步,而无需重写函数



基本上我有类似于这样的同步代码:

def f(.):
...
def call(.):
..Some sync code..
try:
resp = f(..)
...some more transformations of resp...
return ..
except:
..Some error handling..
async def af(.):
...

基本上我想动态更改call的代码,以便它可以调用和等待af函数而不是f。有没有办法解决这个问题?我在 github 上找到了syncit,但这对我来说似乎不是解决方案,因为您必须首先将代码重新设置为异步,然后将其降级以同步。任何帮助将不胜感激。

asyncio世界中,每个协程都可以在其他协程中执行(使用await(,也可以通过事件循环阻塞调用(使用run_until_complete()执行(。

您不能在常规函数中等待协程,因为此函数的调用将是阻塞的,而要等待的协程需要阻塞事件循环执行。这就是asyncio设计的工作方式。

如果您知道call()函数被阻塞并且可以访问f()实现,则可以在运行事件循环中执行协程f()

async def af(.):
...
def f(.):
loop = asyncio.get_event_loop()
return loop.run_until_complete(af())
def call(.):
..Some sync code..
try:
resp = f(..)
...some more transformations of resp...
return ..
except:
..Some error handling..

如果您无法访问f()实现,我相信您将无法更改call()以等待协程(没有一些丑陋的猴子补丁(。

我认为将call()重写为异步将是唯一不错的选择。

下面是混合同步和异步代码的两个选项。

鉴于

import asyncio
import trio
from unsync import unsync

法典

选项 1-trio

给定同步代码 (call(,通过trio(async_div调用异步函数:

# Sync code
def call(x, y):
"""Return an awaited result."""
try:
resp = async_div(x, y)
except ZeroDivisionError:
return "Caught an exception."
return resp

# Wrapper
def async_div(*args):
"""Return results from an async function."""
return trio.run(_async_div, *args)

# Async code
async def _async_div(x, y):
"""Return an async result."""
await trio.sleep(3)
return x / y

选项 2-unsync

@unsync装饰异步代码并调用result()

# Sync code
def call(x, y):
"""Return an awaited result."""
try:
resp = async_div(x, y).result()
except ZeroDivisionError:
return "Caught an exception."
return resp

# Async code
@unsync
async def async_div(x, y):
"""Return an async result."""
await asyncio.sleep(3)
return x / y

演示

在规定的延迟(3 秒(后,结果相同:

call(0, 1)
# 0.0
call(1, 0)
# 'Caught an exception.'

参见

一些其他资源:

  • 关于trio的文档
  • Talk Python 采访trio创建者 N. Smith,了解更多细节以及他对异步编程的哲学。
  • Python 字节第 73 集第 6 项关于快速unsync详细信息
  • 肯尼迪先生关于使用unsync加快请求速度的网络研讨会演示

最新更新