Python - 在某些 class 的非异步 __init__ 中等待异步任务
Python - waiting for asyncio task in non-async __init__ of certain class
我正在制作一个 python class,其中包含一个 __init__
函数和另一个函数,比方说 f
。 __init__
函数不是异步的,但另一个函数是。现在,我正在尝试从 __init__
.
执行另一个函数
我调查过的事情:
- 我找到了一些带有 get_event_loop/get_running_loop 和 run_until_complete 的代码,但这给出了事件循环已经是 运行.
的 RuntimeError
- 我看过关于nest_asyncio的评论,但好像有点脏。
- 此外,我已经使用 create_task 尝试了几种方法,收集并等待。但在这种情况下,代码不会等到任务完全执行。
- 我可以通过先调用构造函数然后用 await 调用第二个函数来拆分它,但我希望在构造函数期间调用第二个函数。
请注意,这是在 Azure Function App 上下文中。
一些示例代码:
import asyncio
class myClass(object):
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
#asyncio.create_task(self.f('abc'))
self.loop = asyncio.get_event_loop()
self.loop.run_until_complete(self.f('abc'))
async def f(self, x):
await asyncio.sleep(1)
self.x = x
if __name__ == '__main__':
cl = myClass(1,2)
print(cl.x)
这在这个简单的上下文中工作得很好,但在更复杂的上下文中就不行了,比如在 Azure Functions 中,我试图在一个函数中使用 myClass,然后想使用它的“x”属性 .
__init__
中的代码不能使用 await
所以你需要做两件事中的一件。
在 __init__
中启动一些异步任务并提供第二种等待它们的方法:
import asyncio
class myClass(object):
""" must await on obj.finish_init() before the object can be used """
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
self._finish_init_promise = asyncio.create_task(self.f('abc'))
#self.loop = asyncio.get_event_loop()
#self.loop.run_until_complete(self.f('abc'))
async def finish_init(self):
await self._finish_init_promise
return self
async def f(self, x):
await asyncio.sleep(1)
self.x = x
async def main():
cl = await myClass(1,2).finish_init()
print(cl.x)
if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(main())
然而,构建一个不完整的对象可能是不可取的(您可能希望有一个布尔值来指示它是否准备就绪),因此另一种方法是提供异步 classmethod
来准备数据然后将该数据传递给构造函数:
import asyncio
class myClass(object):
def __init__(self, arg1, arg2, x):
self.arg1 = arg1
self.arg2 = arg2
self.x = x
@classmethod
async def make(cls, arg1, arg2):
x = await cls.obvious_example_will_be_replaced_with_real_use_case('abc')
return cls(arg1, arg2, x)
@staticmethod
async def obvious_example_will_be_replaced_with_real_use_case(x):
await asyncio.sleep(1)
return x
async def main():
cl = await myClass.make(1,2)
print(cl.x)
if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(main())
我正在制作一个 python class,其中包含一个 __init__
函数和另一个函数,比方说 f
。 __init__
函数不是异步的,但另一个函数是。现在,我正在尝试从 __init__
.
我调查过的事情:
- 我找到了一些带有 get_event_loop/get_running_loop 和 run_until_complete 的代码,但这给出了事件循环已经是 运行. 的 RuntimeError
- 我看过关于nest_asyncio的评论,但好像有点脏。
- 此外,我已经使用 create_task 尝试了几种方法,收集并等待。但在这种情况下,代码不会等到任务完全执行。
- 我可以通过先调用构造函数然后用 await 调用第二个函数来拆分它,但我希望在构造函数期间调用第二个函数。
请注意,这是在 Azure Function App 上下文中。
一些示例代码:
import asyncio
class myClass(object):
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
#asyncio.create_task(self.f('abc'))
self.loop = asyncio.get_event_loop()
self.loop.run_until_complete(self.f('abc'))
async def f(self, x):
await asyncio.sleep(1)
self.x = x
if __name__ == '__main__':
cl = myClass(1,2)
print(cl.x)
这在这个简单的上下文中工作得很好,但在更复杂的上下文中就不行了,比如在 Azure Functions 中,我试图在一个函数中使用 myClass,然后想使用它的“x”属性 .
__init__
中的代码不能使用 await
所以你需要做两件事中的一件。
在 __init__
中启动一些异步任务并提供第二种等待它们的方法:
import asyncio
class myClass(object):
""" must await on obj.finish_init() before the object can be used """
def __init__(self, arg1, arg2):
self.arg1 = arg1
self.arg2 = arg2
self._finish_init_promise = asyncio.create_task(self.f('abc'))
#self.loop = asyncio.get_event_loop()
#self.loop.run_until_complete(self.f('abc'))
async def finish_init(self):
await self._finish_init_promise
return self
async def f(self, x):
await asyncio.sleep(1)
self.x = x
async def main():
cl = await myClass(1,2).finish_init()
print(cl.x)
if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(main())
然而,构建一个不完整的对象可能是不可取的(您可能希望有一个布尔值来指示它是否准备就绪),因此另一种方法是提供异步 classmethod
来准备数据然后将该数据传递给构造函数:
import asyncio
class myClass(object):
def __init__(self, arg1, arg2, x):
self.arg1 = arg1
self.arg2 = arg2
self.x = x
@classmethod
async def make(cls, arg1, arg2):
x = await cls.obvious_example_will_be_replaced_with_real_use_case('abc')
return cls(arg1, arg2, x)
@staticmethod
async def obvious_example_will_be_replaced_with_real_use_case(x):
await asyncio.sleep(1)
return x
async def main():
cl = await myClass.make(1,2)
print(cl.x)
if __name__ == '__main__':
asyncio.get_event_loop().run_until_complete(main())