我想写一个可延迟加载的属性。第一次
__get__()
时可以加载数据,但是__get__()
无法设置为异步。有没有办法等待obj.load()
完成然后返回getter的返回值。
class LazyLoadableProperty:
def __get__(self, obj, owner_class):
if obj is None:
return self
if not self.__loaded:
# task = asyncio.create_task(obj.load())
# while True:
# break when task is finished
self.__loaded = True
return self.__get(obj) # __get() is a costume getter set when init.
...
class A(LazyLoadable):
_name: str
def __init__():
self._name = ""
async def load(): # LazyLoadable is a abc and LazyLoadable.load is an async func.
# load from http
asyncio.sleep(1)
self._name = "Jax"
@LazyLoadableProperty
def name(self):
return self._name
async def main():
a = A()
print(a.name)
asyncio.run(main())
obj.load()
asyncio.get_event_loop().run_until_complete(obj.load())
其他一些方法可能会修改太多我不想使用的现有代码:
__get__()
返回一个 Awaitable 并等待 await a.name
。<subclass>.load()
更改为同步功能。你在这里错过了一个重要的概念。当你的脚本遇到这一行时:
print(a.name)
该值为
a.name
无法立即获得。这就是你想要做的事情的全部意义。考虑一下:当 main
等待时,该值如何变得变得可用?在您的评论中,您说这是通过 HTTP 调用实现的。但要进行 HTTP 调用,您必须执行代码。在单线程异步程序中,此类代码唯一可以执行的位置是在
main
函数之外的 another任务中。为了将控制权交给另一个任务,您需要执行一个等待表达式。在您提供的代码中根本没有等待语句,因此您的方法没有机会工作。
您的主要功能必须如下所示:
async def main():
a = A()
print(await a.name)
await 表达式暂停
main
,直到另一个任务计算 a.name
的值。这可能不是您想要的,但如果 a.name
的值尚不可用,那么确实没有其他逻辑可能性。
正如您从这个小代码片段中看到的,表达式
a.name
将是一个可等待的对象。它也可以是财产——没有任何规则反对这一点。下面的代码,当您运行它时,会延迟一秒钟,然后打印“Jax”。
import asyncio
class A:
_name: str
def __init__(self):
self._name = ""
async def load_name(self):
# load from http
if not self._name:
await asyncio.sleep(1)
self._name = "Jax"
return self._name
@property
async def name(self):
return await self.load_name()
async def main():
a = A()
print(await a.name)
asyncio.run(main())
此脚本执行完全相同的操作。
import asyncio
class A:
_name: str
def __init__(self):
self._name = ""
async def load_name(self):
# load from http
if not self._name:
await asyncio.sleep(1)
self._name = "Jax"
return self._name
@property
def name(self):
return self.load_name()
async def main():
a = A()
print(await a.name)
asyncio.run(main())
第二个版本包含更少的击键。但我个人更喜欢第一个版本,它更清楚地表明属性
name
是一个可等待的对象。
无需创建特殊的类或发明特殊类型的属性。
我还修复了您在几个地方遗漏的
self
参数,并在 await
前面添加了必要的 asyncio.sleep
。