在创建使用asyncio的类时,我发现自己处在属性获取器需要执行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()
这是正确的方法吗?
答案 0 :(得分:3)
简短的回答:不要这样做。
更长的答案:如mentionned in pep8:
避免将属性用于计算昂贵的操作;属性符号使调用者认为访问(相对)便宜。
因此,任何需要IO的东西显然都不适合作为财产的候选人。 FWIW我们不仅期望属性访问便宜,我们还期望它们安全(您是否希望属性访问可能引发IOError,数据库错误,套接字错误或类似的东西?)
FWIW,您提到“正在等待财产feels unusual”,这应该已经回答了您的问题。实际上,就我而言,“异步属性”的想法让我感到震惊,因为它完全是疯狂的-属性(在语义上是关于对象状态的),而我只是无法理解“异步”的概念州”。
答案 1 :(得分:3)
这是正确的方法吗?
除了样式问题,即属性是否应该是否返回等待状态。 other answer出于常识而反对这种做法,但也基于PEP 8的以下引用:
避免将属性用于计算昂贵的操作;属性符号使调用者认为访问(相对)便宜。
按照书面规定,这并不意味着属性不应返回等待对象,原因有两个:
使用属性符号访问属性非常便宜,因为它只会创建一个 awaitable (如果是协程,则为协程对象)。只有当您等待生成的对象时,您才能挂起该对象,并使用await
对其进行明显标记。
等待某些事情在计算上并不昂贵-实际上,在协程中禁止执行某些计算上昂贵的事情,因为它会干扰其他任务。 await
会立即返回值,或者会挂起封闭的协程。后者当然可以花费 时间(但这是使用await
的全部时间),但是就CPU而言,绝对不是昂贵。
我相信PEP8警告背后的想法是,简单的属性访问不应导致状态更改或长时间的停顿。如上所述,这也适用于异步属性,因为访问只为您提供协程对象。另一方面,如果然后继续显式await
该对象,则不仅是允许,而且实际上是请求可等待对象的解决方案。这与<some list>.append
不做任何事情就为您提供绑定方法对象的方式没什么不同,但是如果您随后调用该对象,则调用将更改列表。
总而言之,如果从属性返回awaitable“感觉错误”,则不要这样做,而应使用方法。但是据我所知,PEP 8并不反对这种做法。