[英]What is a pythonic way to have property getters without setters with python dataclasses?
I have a situation where I need to store variables a,b, and c together in a dataclass, where c = f(a,b)
and a,b can be mutated.我有一种情况,我需要将变量 a、b 和 c 一起存储在一个数据类中,其中
c = f(a,b)
和 a,b 可以突变。 I need c
to be displayed along with a and b when printing the object, and c
cannot be changed except through changing a
or b
.打印 object 时,我需要
c
与 a 和 b 一起显示,并且c
不能更改,除非通过更改a
或b
。 I felt the best way to do this was to have c
be a value created with a property.我觉得做到这一点的最好方法是让
c
成为使用属性创建的值。
Minimal example of what I tried to use:我尝试使用的最小示例:
@dataclass
class Demo:
a:int
b:int
c:int = field(init=False)
@property
def c(self) -> int:
return self.a * self.b
However, calling this results in AttributeError: can't set attribute
.但是,调用它会导致
AttributeError: can't set attribute
。 I suspect the code is trying to set c to field(init=False)
, and failing because c doesn't have a setter.我怀疑代码试图将 c 设置为
field(init=False)
,但由于 c 没有设置器而失败。
Options I've considered (not counting alternatives I tried that makes the code crash in a different manner):我考虑过的选项(不包括我尝试过的使代码以不同方式崩溃的替代方案):
c
in initc
@c.setter
that does nothing@c.setter
sys._getframe().f_back.f_code
in the setter to raise an error if the caller is not __init__
(function at endnote)__init__
(尾注中的函数),请在 setter 中使用sys._getframe().f_back.f_code
引发错误
c
be created in __post_init__
, and use dataclasses.replace
to modify a and b.c
中创建__post_init__
,并使用dataclasses.replace
修改 a 和 b。
Demo
(well, the actual thing, not the minimal example) require it to be mutable.Demo
的其他用途(嗯,实际的东西,而不是最小的例子)要求它是可变的。 endnote:尾注:
@c.setter
def c(self, val):
caller = sys._getframe().f_back.f_code.co_name
if caller != '__init__':
raise ValueError # or some other error
If you want c
to be a computed property, then take out the c
field entirely and just leave the property:如果您希望
c
成为计算属性,则完全取出c
字段并保留该属性:
@dataclass
class Demo:
a:int
b:int
@property
def c(self) -> int:
return self.a * self.b
Fields are for stored data, which c
should not be.字段用于存储数据,
c
不应该是。
If you want c
to show up in repr
, you'll have to write the __repr__
method yourself.如果您希望
c
出现在repr
中,您必须自己编写__repr__
方法。 If later code needs c
to show up in fields
, then you probably need to rework your code at a deeper level, and possibly switch to something other than dataclasses
.如果以后的代码需要
c
显示在fields
中,那么您可能需要在更深层次上重新编写代码,并可能切换到dataclasses
以外的东西。
Your method c
overwrites your field c
-- the original field
declaration is lost, including the init=False
.您的方法
c
覆盖您的字段c
- 原始field
声明丢失,包括init=False
。
Try changing the order and using the default=
parameter.尝试更改顺序并使用
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
Output from Python 3.8.0: Output 来自 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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.