英文:
Proper use of dataclass decorator
问题
Happy clinet is satisfied: [True, True]
英文:
Hello everyone,
I'm trying to create my own data class utilizing the @dataclass decorator.
Variables that I directly give values end up initialized propperly.
However the variables satisfied and loud keep their default values.
How could I force python to recalculate satisfied and loud values by given arguments?
I have attached a small code and its output representing my issue.
Code:
from dataclasses import dataclass, astuple
class BaseSatisfaction():
satisfied = list[bool]
loud = bool
@dataclass
class ClientSatisfaction(BaseSatisfaction):
food_tasty: bool = False
served_quickly: bool = False
restaurant_noisiness_dB: None | float = None
satisfied = [food_tasty, served_quickly]
loud = 0 if restaurant_noisiness_dB is None else restaurant_noisiness_dB > 100
happy_client = ClientSatisfaction(food_tasty=True, served_quickly = True)
rage_client = ClientSatisfaction(restaurant_noisiness_dB = 120)
print("======== Happy Client =========")
print(happy_client.__dict__.items())
print(f"Happy clinet is satisfied: {happy_client.satisfied}")
print(f"How loud was it?: {happy_client.loud}")
print("======== Rage Client =========")
print(rage_client.__dict__.items())
print(f"Rage clinet is satisfied: {rage_client.satisfied}")
print(f"How loud was it?: {rage_client.loud}")
Output:
======== Happy Client =========
dict_items([('food_tasty', True), ('served_quickly', True), ('restaurant_noisiness_dB', None)])
Happy clinet is satisfied: [False, False]
Was it too loud?: 0
======== Rage Client =========
dict_items([('food_tasty', False), ('served_quickly', False), ('restaurant_noisiness_dB', 120)])
Rage clinet is satisfied: [False, False]
Was it too loud?: 0
It should return:
Happy clinet is satisfied: [True, True]
答案1
得分: 3
在Python中,类的主体中的代码仅在类被定义时执行一次。这包括所有的赋值操作。这意味着对satisfied和loud的赋值是在类定义时执行的,并使用类主体中定义的默认值。这就是为什么在创建实例时初始值不会被更新的原因。
为了解决这个问题,你应该为每个实例计算satisfied和loud的值。你可以使用__post_init__方法,这是由@dataclass装饰器提供的一个特殊方法,它在实例初始化之后自动调用。
以下是你的代码的修复版本:
from dataclasses import dataclass, astuple
@dataclass
class ClientSatisfaction:
food_tasty: bool = False
served_quickly: bool = False
restaurant_noisiness_dB: None | float = None
def __post_init__(self):
self.satisfied = [self.food_tasty, self.served_quickly]
self.loud = 0 if self.restaurant_noisiness_dB is None else self.restaurant_noisiness_dB > 100
happy_client = ClientSatisfaction(food_tasty=True, served_quickly=True)
rage_client = ClientSatisfaction(restaurant_noisiness_dB=120)
print("======== Happy Client =========")
print(happy_client.__dict__.items())
print(f"Happy client is satisfied: {happy_client.satisfied}")
print(f"How loud was it?: {happy_client.loud}")
print("======== Rage Client =========")
print(rage_client.__dict__.items())
print(f"Rage client is satisfied: {rage_client.satisfied}")
print(f"How loud was it?: {rage_client.loud}")
使用这段代码,satisfied和loud字段会针对每个实例计算,使用实例自己的字段值。这将给出你期望的结果。请注意,在这种情况下,你不需要BaseSatisfaction类。
英文:
In Python, the code in the body of the class is executed only once, when the class is defined. This includes all assignments. This means that the assignment to satisfied and loud is performed when the class is defined, and uses the default values defined in the class body. This is why the initial values are not updated when an instance is created.
To solve this problem, you should compute the values of satisfied and loud for each instance. You can use the post_init method, which is a special method provided by the @dataclass decorator that is automatically called after the instance has been initialized.
Here is a fixed version of your code:
from dataclasses import dataclass, astuple
@dataclass
class ClientSatisfaction:
food_tasty: bool = False
served_quickly: bool = False
restaurant_noisiness_dB: None | float = None
def __post_init__(self):
self.satisfied = [self.food_tasty, self.served_quickly]
self.loud = 0 if self.restaurant_noisiness_dB is None else self.restaurant_noisiness_dB > 100
happy_client = ClientSatisfaction(food_tasty=True, served_quickly=True)
rage_client = ClientSatisfaction(restaurant_noisiness_dB=120)
print("======== Happy Client =========")
print(happy_client.__dict__.items())
print(f"Happy client is satisfied: {happy_client.satisfied}")
print(f"How loud was it?: {happy_client.loud}")
print("======== Rage Client =========")
print(rage_client.__dict__.items())
print(f"Rage client is satisfied: {rage_client.satisfied}")
print(f"How loud was it?: {rage_client.loud}")
With this code, the satisfied and loud fields are calculated for each instance, using the instance's own field values. This gives you the expected results. Note that you do not need the BaseSatisfaction class in this case.
答案2
得分: 1
如Pierre Vermot在上面所说,这些值仅在初始化时设置。
由于您显然希望根据两个其他值(在它们初始化后可能会更改)动态生成该属性,我会将.satisfied
实现为@property
:这样,它将随时从当前值计算满意度,就像一个函数但像属性一样访问:
从数据类中删除satisfied
属性,然后添加此方法:
@property
def satisfied(self):
return [self.food_tasty, self.served_quickly]
使用它:
>>> satis = ClientSatisfaction(True, True)
>>> satis.satisfied
[True, True]
>>> satis.food_tasty = False
>>> satis.satisfied
[False, True]
英文:
As Pierre Vermot said above, these values are only set on initialization.
Since you apparently want to dynamically generate that attribute as a function of the two others (which might be altered after their initialization), I'd implement .satisfied
as a @property
: That way, it will at any time compute the satisfaction from the current values, like a function but accessed like an attribute:
Remove the satisfied
attribute from the dataclass, and add this method instead:
@property
def satisfied(self):
return [self.food_tasty, self.served_quickly]
Using it:
>>> satis = ClientSatisfaction(True, True)
>>> satis.satisfied
[True, True]
>>> satis.food_tasty = False
>>> satis.satisfied
[False, True]
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论