代码之家  ›  专栏  ›  技术社区  ›  user3139545

只运行@property一次

  •  -2
  • user3139545  · 技术社区  · 6 年前

    我有以下例子:

    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 回复  |  直到 6 年前
        1
  •  5
  •   abarnert    6 年前

    你想利用 self.value 属性的名称,以及在封面下用于存储该属性的属性的名称。这没道理,你得给它起个不同的名字。由于该属性应为“private”,并且只能通过属性访问,因此通常需要使用下划线前缀。

    换句话说,在例子中做完全相同的事情。 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 ,由类的所有实例共享。

    self._value = result 在方法体不更改类属性的情况下,它创建 实例属性 同名。

    实例属性 阴影 类属性。也就是说,当你试图访问 self._value if return 语句,它查找实例属性,如果不存在类属性,则返回到类属性。所以,类属性 None 值作为实例属性的默认值。

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

    做同样的事情的另一种方法是强制实例总是有一个 γ值 属性,通过在 __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
    

    虽然乍一看这有点冗长和复杂(并且需要知道 爱因斯坦 ,它确实避免了混淆类和实例属性的可能性,因此如果您与新手共享代码,他们更有可能遵循它。

    或者,您可以使用 getattr 查看属性是否存在,因此不需要回退类属性或 爱因斯坦 ,但这通常不是你想做的。


    退一步,你甚至可以替换 财产 用一个描述符替换第一个查找函数,但是如果你不知道它是什么意思(即使在读了 HowTo ),你可能也不想这么做。