使用 python 数据类拥有属性 getters 而没有 setters 的 pythonic 方法是什么?

rea*_*oy7 2 python python-dataclasses

我遇到一种情况,我需要将变量 a、b 和 c 一起存储在数据类中,其中c = f(a,b)a、b 可以发生变化。c打印对象时,I 需要与 a 和 b 一起显示,并且c只能通过更改a或来更改b。我认为做到这一点的最好方法就是c通过财产创造价值。

我尝试使用的最小示例:

@dataclass
class Demo:
    a:int
    b:int
    c:int = field(init=False)

    @property
    def c(self) -> int:
        return self.a * self.b
Run Code Online (Sandbox Code Playgroud)

但是,调用此结果会导致AttributeError: can't set attribute. 我怀疑代码试图将 c 设置为field(init=False),但失败了,因为 c 没有设置器。

我考虑过的选项(不包括我尝试过的使代码以不同方式崩溃的替代方案):

  • 没有c在 init 中
    • 在 repr、字段等中不显示 c,这会导致进一步的问题
  • 有一个@c.setter什么都不做的
    • 这在技术上是可行的,但涉及编写看似没有任何作用的样板代码。
    • 拥有一个永远不会被我编写的任何代码显式调用但使程序中断被删除的函数感觉危险且不干净。
    • 我还希望程序崩溃,而不是如果有人尝试直接修改 c 则默默地不执行任何操作。
  • 如上所述,但如果调用者不是(尾注中的函数), sys._getframe().f_back.f_code则在 setter 中使用会引发错误__init__
    • 仅是 Cpython 实现细节。
    • 一般来说,使用标记为私有的函数是一个坏主意。
  • 让 Demo 不可变,c在 中创建__post_init__,并用于dataclasses.replace修改 a 和 b。
    • 这是在真空中工作的,但其他用途Demo(嗯,实际的东西,不是最小的例子)要求它是可变的。

尾注:

    @c.setter
    def c(self, val):
        caller = sys._getframe().f_back.f_code.co_name
        if caller != '__init__':
            raise ValueError # or some other error
Run Code Online (Sandbox Code Playgroud)

use*_*ica 5

如果您想c成为计算属性,则完全取出该c字段并保留该属性:

@dataclass
class Demo:
    a:int
    b:int

    @property
    def c(self) -> int:
        return self.a * self.b
Run Code Online (Sandbox Code Playgroud)

字段用于存储数据,但c不应该如此。

如果你想出c现在 中repr,你必须__repr__自己编写方法。如果以后的代码需要c显示在 中fields,那么您可能需要在更深层次上重新编写代码,并且可能切换到dataclasses.