【问题标题】:Having @property only run once让@property 只运行一次
【发布时间】:2018-11-28 18:12:06
【问题描述】:

我有以下例子:

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

这是有道理的,但是做这样的事情的惯用方式是什么?我不只是想使用不同的名称。

【问题讨论】:

  • 这段代码的意义为零。为什么你把value=None 作为类变量,只是为了立即用property 替换它?您无法设置属性,因为您尚未定义 setter。此外,if not value: 会抛出 NameError,因为未定义 value,因此此代码不会到达 AttributeError

标签: python


【解决方案1】:

您正在尝试使用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类属性。也就是说,当您尝试在ifreturn 语句中访问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 之后),您可能不知道也想这样做。

【讨论】:

  • @juanpa.arrivillaga 我已经添加了一些解释。
  • 另一种选择是使用不带__set____delete__ 方法的描述符,因此您可以简单地设置self.__dict__['name'] = value,并且在后续访问时不再调用描述符。
  • @MartijnPieters 是的,但我不知道我是否想尝试向那些在基础方面遇到问题的人解释这一点,我不喜欢给人们提供他们可以复制和复制的魔法调用粘贴没有任何理解的希望,并且永远无法调试或修改。如果您认为可以解释,请单独写一个答案。
  • @abarnert:很公平;只是想把它也放在那里。也要为未来的读者写作,而不仅仅是当前的 OP,然后考虑是否可以使非数据描述符选项足够清晰。或者只是指向 PyDanny's cached_property project 并进行简短介绍。
  • @MartijnPieters 我不知道cached_property;这似乎值得包括在内。但至于为未来的读者写作——老实说,我不认为任何理解描述符的人会在这个问题中寻找任何东西,而且我认为尝试教授描述符在已经——关于使用@property_private 属性等的基础知识的长答案(我认为即使解释类与实例属性也会推动它……)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-09
  • 2011-12-13
  • 2017-10-17
相关资源
最近更新 更多