【问题标题】:How to initialize attributes calculated from other attributes in python classes如何初始化从python类中的其他属性计算的属性
【发布时间】:2020-09-01 14:53:09
【问题描述】:

我想从其他属性计算一个类中的一个属性。

我希望这个属性是私有的,所以我想把它变成一个属性。

我知道仍然需要在类的 init 块中调用一个属性,但是我没有找到一种优雅的方式来编写它。

一些丑陋的代码可以满足我的需要:

class ABC:
    def __init__(self, t=0):
        self.attrA = t
        self._attrB = None

    @property
    def attrB(self):
        return self._attrB

    @attrB.setter
    def attrB(self, value):
        self._attrB = 2*self.attrA

obj = ABC(10)
print(obj.attrB)

问题:如何避免在 init 中使用虚拟的“无”值声明 attrB?

编辑到以前的帖子:

    1234563 attrB 每次被调用都是不行的
  • 我知道缓存属性调用也是一种选择,但是没有更简单的方法是拥有一个真实的属性吗?

【问题讨论】:

  • 附带说明,ABC 不是一个好的类名称,即使是在一个简单的示例中,因为 abc 模块和 abc.ABC... 甚至 'A` 更好。
  • 有道理会记住的:)

标签: python properties attributes


【解决方案1】:

您根本不需要self._attrB,或 attrB.setter,因为您的attrB getter 可以返回任何您想要的attrB 的值(在此如果是attrA的两倍):

class ABC:
    def __init__(self, t=0):
        self.attrA = t

    @property
    def attrB(self):
        return 2 * self.attrA

obj = ABC(10)
print(obj.attrB)

你的attrB setter 是不必要的一个很好的线索是它忽略了你传递给它的value!将此逻辑放在 getter 中会更简单,因为它不依赖于任何尚未存储在类中的数据。

(编辑)如果你用来生成attrB的逻辑比较复杂,你可以这样缓存它:

from functools import lru_cache

class ABC:
    def __init__(self, t=0):
        self.attrA = t

    @property
    def attrB(self):
        return self._attrB(self.attrA)

    @lru_cache
    def _attrB(self, attrA):
        return 2 * self.attrA

@lru_cache 将根据attrA 的值缓存_attrB 的返回值,因此如果attrA 发生变化,它将重新计算,否则从缓存中提供值。

如果您不想在attrA 更改时更改attrB,那么您可以简单地使用cached_property,它会在您第一次访问它时计算值,然后使用缓存副本:

from functools import cached_property

class ABC:
    def __init__(self, t=0):
        self.attrA = t

    @cached_property
    def attrB(self):
        return 2 * self.attrA

显然,如果您不希望 attrA 本身在初始化后发生变化,您可以将其设为私有变量。

【讨论】:

  • 谢谢,我想到了,即只使用属性获取器,但是在这种情况下,attrB 会被缓存,还是每次调用 obj.attrB 时都会重新计算?
  • 在我的用例中,这些属性是可能很长的列表,我希望必须多次重新计算 attrB
  • 使用该实现,每次都会重新计算,但没有理由不能在 getter 中进行缓存。我敢打赌,您甚至可以只使用@functools.lru_cache 而不必实际实现自己的缓存。 :)
  • 好的,谢谢,如果标准方法是使用属性作为 getter + 进行缓存,那么这对我有用,只是想检查是否有其他方法
  • 您也可以使用其他方法进行重新计算,但是在带有虚假参数的 setter 中执行此操作是一种错误的模式 IMO,因为它通常暗示如果您进行分配,该属性将采用您分配的任何值,而不是忽略它并获取其他值。您希望它只是一个名为 refresh_attr() 或类似名称的无参数方法。
【解决方案2】:

关于您帖子的编辑部分:

atrrB 属性是一个属性。通过将其设为属性,您只需添加在访问此属性以进行读取或写入或两者兼而有之时运行的代码。

属性是descriptor的可能类型之一

缓存属性的昂贵计算是一个很好的解决方案。

如果您从 Python 开始,描述符是一项高级功能。但是使用属性甚至缓存都非常简单。

【讨论】:

  • 谢谢,更新为“存储”属性以澄清。如果没有比缓存属性结果更简单的解决方案,那么我会这样做。事实上,根据我在 Descriptors 上所读到的内容,可能还有其他明确使用 setattrt 的方法,但我现在将保留更简单的方法
猜你喜欢
  • 2016-03-03
  • 2017-09-29
  • 1970-01-01
  • 2020-02-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-21
相关资源
最近更新 更多