@property只运行一次

时间:2018-06-19 17:44:21

标签: python

我有以下示例:

class A:
    value = None
    @property
    def value(self):
        if not value:
            result = <do some external call>
            self.value = result
        return self.value

但是,由于我遇到了异常,所以这不起作用:

AttributeError: can't set attribute

这很有意义,但是做这样的事情的惯用方式是什么?我不只是想使用其他名称。

1 个答案:

答案 0 :(得分:5)

您正尝试将self.value用作属性的名称,以及在封面下用于存储该属性的属性的名称。那没有道理;您需要给它起一个不同的名字。由于该属性是“私有”的,并且只能通过该属性进行访问,因此通常需要使用下划线前缀。

换句话说,做与the docs for property中的示例完全相同的事情。

当然,必须更改if not value:才能通过self访问相同的属性,而不是某些具有相同名称的局部变量。

class A:
    _value = None
    @property
    def value(self):
        if not self._value:
            result = <do some external call>
            self._value = result
        return self._value

另一种选择是使用为您执行此操作的库。正如Martijn Pieters在评论中指出的那样,cached-property可以为您提供所需的确切信息。并且它在各种您可能从未想到的边缘情况下(例如线程和异步)经过了测试,并且功能强大。此外,它还提供了出色的文档和可读的源代码,以解释其工作原理。


无论如何,_value = None正确的原因可能并不立即显而易见。

类定义中的

_value = None创建一个名为_value类属性,该属性由该类的所有实例共享。

方法主体中的

self._value = result不会更改该类属性,它会创建一个具有相同名称的 instance属性

实例属性 shadow 类属性。也就是说,当您尝试访问self._valueif语句中的return时,它会查找一个实例属性,如果不存在,则会退回到类属性。因此,类属性的None值用作实例属性的默认值。

对于提供默认属性值,这是一个非常常见的习惯用法,但是在某些情况下(例如,可变值)可能会造成混淆。

执行相同操作的另一种方法是通过在_value__new__方法中设置实例,强制实例始终具有__init__属性:

class A:
    def __init__(self):
        self._value = None
    @property
    def value(self):
        if not self._value:
            result = <do some external call>
            self._value = result
        return self._value

尽管乍看之下它比较冗长和复杂(并且需要了解__init__),但确实避免了混淆类和实例属性的可能性,因此,如果您要与新手,他们很有可能会遵循它。

或者,您可以使用getattr来查看属性是否存在,因此不需要后备类属性或__init__,但这通常不是您想要执行的操作。


退后一步,您甚至可以将描述符中的property替换为可在第一次查找时替换自身的描述符,但是即使您不知道这意味着什么(即使在阅读HowTo之后也可以),可能也不想这样做。