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

时间:2019-03-04 13:29:50

标签: python python-3.x python-asyncio

在创建使用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()

这是正确的方法吗?

2 个答案:

答案 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并不反对这种做法。