您正在尝试使用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 在 cmets 中指出的那样,cached-property 为您提供了您想要的东西。它在您可能从未想过的各种边缘情况(例如线程和异步)中经过测试且稳健。它还有很好的文档和可读的源代码,可以解释它是如何工作的。
无论如何,为什么_value = None 是正确的可能不是很明显。
_value = None 在类定义中创建一个名为_value 的类属性,由类的所有实例共享。
方法体中的
self._value = result 不会更改该类属性,它会创建一个具有相同名称的 instance 属性。
实例属性shadow类属性。也就是说,当您尝试在if 和return 语句中访问self._value 时,它会查找实例属性,如果不存在则回退到类属性。因此,类属性的None 值作为实例属性的默认值。
对于提供默认属性值,这是一个非常常见的习惯用法,但在某些情况下(例如,使用可变值)可能会造成混淆。
做同样事情的另一种方法是通过在__new__ 或__init__ 方法中设置它来强制实例始终具有_value 属性:
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 之后),您可能不知道也想这样做。