我遇到一种情况,我需要将变量a,b和c一起存储在一个数据类中,其中c = f(a,b)
和a,b可以被突变。打印对象时,我需要c
与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
但是,调用此命令将导致AttributeError: can't set attribute
。我怀疑代码尝试将c设置为field(init=False)
,但由于c没有设置器而失败。
我考虑过的选项(不计算我尝试过的使代码以不同方式崩溃的替代方法):
c
@c.setter
不执行任何操作的
sys._getframe().f_back.f_code
,则在设置器中使用__init__
引发错误(尾注中的功能)
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
答案 0 :(得分:2)
如果您希望c
是一个计算属性,则完全取出c
字段并保留该属性:
@dataclass
class Demo:
a:int
b:int
@property
def c(self) -> int:
return self.a * self.b
字段用于存储数据,c
不应该。
如果您希望c
显示在repr
中,则必须自己编写__repr__
方法。如果以后的代码需要c
才能显示在fields
中,那么您可能需要在更深的层次上重新编写代码,并可能切换到dataclasses
以外的地方。
答案 1 :(得分:1)
您的方法c
会覆盖您的字段c
-原始的field
声明(包括init=False
)都将丢失。
尝试更改顺序并使用default=
参数。
from dataclasses import dataclass, field
@dataclass
class Demo:
@property
def c(self) -> int:
return self.a * self.b
a: int
b: int
c: int = field(init=False, default=c)
d = Demo(5, 10)
print(d)
d.a = 10
print(d)
d.c = 4 # generates an AttributeError
Python 3.8.0的输出:
Demo(a=5, b=10, c=50)
Demo(a=10, b=10, c=100)
Traceback (most recent call last):
...
File "/home/rob/src/plot/dc.py", line 20, in <module>
d.c = 4 # generates an AttributeError
AttributeError: can't set attribute