Jac*_*son 5 python python-3.x python-asyncio
在创建一个使用 asyncio 的类时,我发现自己处于一种属性 getter 需要进行 io 操作的情况。因此该函数应该是一个协程。然而,等待财产感觉不寻常。
这是我的意思的最小工作示例。代码有效并运行。
import asyncio
class Person:
"""A class that represents a person"""
def __init__(self, forename, surname):
self.forename = forename
self.surname = surname
@property
async def fullname(self):
"""Perform an io operation and return something.
This could be looking something up in a database for example.
"""
await asyncio.sleep(0.1)
return f"{self.forename} {self.surname}"
async def main():
john = Person("John", "Smith")
# Let's print out the forename here, using the standard property format
print(john.forename)
# When printing the full name we must instroduce an await, which feels awkward.
print(await john.fullname)
# Start the loop and run the main function
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
Run Code Online (Sandbox Code Playgroud)
这是这样做的正确方法吗?
这是这样做的正确方法吗?
确实如此,除了属性是否应该返回可等待的风格问题。另一个答案基于常识反对这种做法,但也基于PEP 8的以下引用:
避免使用属性进行计算量大的操作;属性表示法使调用者相信访问(相对)便宜。
正如所写,这并不意味着属性不应返回可等待的值,原因有两个:
使用属性表示法访问属性非常便宜,因为它只创建一个等待对象(如果是协程,则为协程对象)。只有当您等待结果对象时,您才能挂起,并且使用await.
等待某件事的计算成本并不高——事实上,在协程中执行计算成本高昂的事情是被禁止的,因为它会干扰其他任务。Anawait要么立即返回值,要么挂起封闭的协程。后者肯定会花费时间(但这就是使用 的全部意义),但就 CPU 而言await绝对不贵。
我相信 PEP8 警告背后的想法是简单的属性访问不应导致状态更改或长时间暂停。如上所述,这也适用于异步属性,因为访问仅提供协程对象。另一方面,如果您继续显式地访问await该对象,那么您不仅允许,而且实际上请求可等待的解决方案。这与不执行任何操作就提供绑定方法对象的方式没有太大区别<some list>.append,但如果您随后调用该对象,则该调用将更改列表。
总之,如果从属性返回可等待“感觉不对”,那么就不要这样做,而是使用方法。但据我所知,PEP 8 并不反对这种做法。