使用 asyncio 时应该如何创建属性?

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)

这是这样做的正确方法吗?

bru*_*ers 10

简短的回答:不要这样做。

更长的答案:如pep8 中提到的

避免使用属性进行计算量大的操作;属性表示法使调用者相信访问(相对)便宜。

所以任何需要 IO 的东西显然都不是属性的候选者。FWIW,我们不仅期望属性访问是廉价的,我们还期望它们是安全的(您期望属性访问可能引发 IOError、数据库错误、套接字错误或类似的错误吗?)

FWIW,你提到“等待财产感觉不寻常”,这应该已经回答了你的问题。实际上,就我而言,“异步属性”的概念让我觉得完全疯狂——属性(语义上)是关于对象的状态,而我只是无法理解“异步属性”的概念。状态”。


use*_*342 7

这是这样做的正确方法吗?

确实如此,除了属性是否应该返回可等待的风格问题。另一个答案基于常识反对这种做法,但也基于PEP 8的以下引用:

避免使用属性进行计算量大的操作;属性表示法使调用者相信访问(相对)便宜。

正如所写,这并不意味着属性不应返回可等待的值,原因有两个:

  • 使用属性表示法访问属性非常便宜,因为它只创建一个等待对象(如果是协程,则为协程对象)。只有当您等待结果对象时,您才能挂起,并且使用await.

  • 等待某件事的计算成本并不高——事实上,在协程中执行计算成本高昂的事情是被禁止的,因为它会干扰其他任务。Anawait要么立即返回值,要么挂起封闭的协程。后者肯定会花费时间(但这就是使用 的全部意义),但就 CPU 而言await绝对不贵。

我相信 PEP8 警告背后的想法是简单的属性访问不应导致状态更改或长时间暂停。如上所述,这也适用于异步属性,因为访问仅提供协程对象。另一方面,如果您继续显式地访问await该对象,那么您不仅允许,而且实际上请求可等待的解决方案。这与不执行任何操作就提供绑定方法对象的方式没有太大区别<some list>.append,但如果您随后调用该对象,则该调用将更改列表。

总之,如果从属性返回可等待“感觉不对”,那么就不要这样做,而是使用方法。但据我所知,PEP 8 并不反对这种做法。

  • 这是一个有趣的 POV。请注意:pep8 确实没有明确警告“异步属性”,但当时 Python 也没有适当的协程...... (2认同)