gpt4 book ai didi

python - 有没有办法从 C++ 调用 `async` python 方法?

转载 作者:太空狗 更新时间:2023-10-29 18:07:27 27 4
gpt4 key购买 nike

我们有一个使用 asyncio 和协程(async 方法和 awaits)的 python 代码库,我想做的是调用一个这些方法来自已被拉入 python 的 C++ 类(使用 pybind11)

假设有这段代码:

class Foo:
async def bar(a, b, c):
# some stuff
return c * a

假设代码是从 python 调用的,并且有一个 io 循环处理这个,在某个时候,代码会进入 C++ 领域,在那里需要调用这个 bar 方法 - 如何await 这个在 C++ 中的结果?

最佳答案

用 C++ 实现 Python 协程是可能的,但需要一些工作。您需要执行解释器(编译器在静态语言中)通常为您执行的操作,并将您的异步函数转换为状态机。考虑一个非常简单的协程:

async def coro():
x = foo()
y = await bar()
baz(x, y)
return 42

调用 coro() 不会运行它的任何代码,但它会生成一个 awaitable 对象,该对象可以多次启动然后恢复。 (但您通常不会看到这些操作,因为它们是由事件循环透明地执行的。)可等待对象可以以两种不同的方式响应:1) 挂起,或 2) 指示它已完成。

在协程内 await 实现暂停。如果协程是用生成器实现的,y = await bar() 会脱糖到:

# pseudo-code for y = await bar()

_bar_iter = bar().__await__()
while True:
try:
_suspend_val = next(_bar_iter)
except StopIteration as _stop:
y = _stop.value
break
yield _suspend_val

换句话说,await 会挂起(让出)只要等待的对象还在。等待的对象通过引发 StopIteration 并通过在其 value 属性中走私返回值来表示它已完成。如果 yield-in-a-loop 听起来像 yield from,那您就完全正确了,这就是为什么 await 经常被用术语描述的原因 yield 来自。但是,在 C++ 中我们没有 yield ( yet ),因此我们必须将上述内容集成到状态机中。

要从头开始实现async def,我们需要一个满足以下约束的类型:

  • 在构建时不会做太多事情——通常它只会存储它收到的参数
  • 有一个返回可迭代对象的__await__方法,它可以只是self
  • 有一个返回迭代器的__iter__,它又可以是self
  • 有一个 __next__ 方法,其调用实现了状态机的一个步骤,返回表示暂停,引发 StopIteration 表示完成。

__next__ 中上述​​协程的状态机将由三个状态组成:

  1. 第一个,当它调用foo()同步函数时
  2. 当它一直等待 bar() 协程时的下一个状态,只要它挂起(传播挂起)给调用者。一旦 bar() 返回一个值,我们可以立即继续调用 baz() 并通过 StopIteration 异常返回该值。
  3. 最终状态,它只是引发一个异常,通知调用者协程已用完。

所以上面显示的 async def coro() 定义可以被认为是以下语法糖:

class coro:
def __init__(self):
self._state = 0

def __iter__(self):
return self

def __await__(self):
return self

def __next__(self):
if self._state == 0:
self._x = foo()
self._bar_iter = bar().__await__()
self._state = 1

if self._state == 1:
try:
suspend_val = next(self._bar_iter)
# propagate the suspended value to the caller
# don't change _state, we will return here for
# as long as bar() keeps suspending
return suspend_val
except StopIteration as stop:
# we got our value
y = stop.value
# since we got the value, immediately proceed to
# invoking `baz`
baz(self._x, y)
self._state = 2
# tell the caller that we're done and inform
# it of the return value
raise StopIteration(42)

# the final state only serves to disable accidental
# resumption of a finished coroutine
raise RuntimeError("cannot reuse already awaited coroutine")

我们可以使用真正的 asyncio 测试我们的“协程”是否工作:

>>> class coro:
... (definition from above)
...
>>> def foo():
... print('foo')
... return 20
...
>>> async def bar():
... print('bar')
... return 10
...
>>> def baz(x, y):
... print(x, y)
...
>>> asyncio.run(coro())
foo
bar
20 10
42

剩下的部分就是用Python/C或者pybind11编写coro

关于python - 有没有办法从 C++ 调用 `async` python 方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54553907/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com