3

I am working with frozen dataclasses in Python. My goal is to create a dataclass with its frozen attributes. However, one of the attributes is a function of the other ones. I want my class to be frozen (once it has been instantiated it can't be changed), but I can't find a way to specify the value of an attribute inside the class with a method.

As an example, let's suppose to have the following simple class:

@dataclasses.dataclass(frozen=True)
class Car:
    color: str
    size: int
    model: str
    price: float

The price of the car is function of its color, size and model. As an example:

def _compute_price(self, color, size, model):

    if color == 'red':
       color_surplus = 10
    else:
       color_surplus = 0

    if size > 10:
       size_surplus = 10
    else:
       size_surplus = 5

    if model == 'mustang':
       model_surplus = 100
    else:
       model_surplus = 0

    self.price = color_surplus + size_surplus + model_surplus

I would like to create my car like:

car = Car('red', 15, 'mustang') 

And automatically use the above sample function inside my class to initialize the missing price attribute. However, as soon as I specify self.price, Python tells me that it is a read only attribute and thus non-assignable. I also tried to use the __init__ function, but I still have the same problem:

@dataclasses.dataclass(frozen=True)
class Car:

    def __init__(self,  color: str, size: int, model: str, price: float):
        
        self.color = color #error here
        ...
        ...

    

Any solutions? My goal is to use a frozen dataclass while being able to specify an attribute with a private function (included in the class) of the other ones

Mattia Surricchio
  • 1,362
  • 2
  • 21
  • 49
  • Does this answer your question? [How to set the value of dataclass field in \_\_post\_init\_\_ when frozen=True?](https://stackoverflow.com/questions/53756788/how-to-set-the-value-of-dataclass-field-in-post-init-when-frozen-true) – rv.kvetch Nov 03 '21 at 15:54
  • Isn't there any cleaner solution? The proposed one seems to have many issues – Mattia Surricchio Nov 03 '21 at 16:03
  • well, the cleaner solution would be to not use `frozen=True`, I suppose. – rv.kvetch Nov 03 '21 at 16:07
  • 2
    I thought about it, but I need a way to avoid external changes in the dataclass instance once it has been created – Mattia Surricchio Nov 03 '21 at 16:08

1 Answers1

3

Make price a property, wich are read only if you don't create a setter:

@property
def price(self):

    if self.color == 'red':
        color_surplus = 10
    else:
       color_surplus = 0

    if self.size > 10:
       size_surplus = 10
    else:
       size_surplus = 5
    if self.model == 'mustang':
       model_surplus = 100
    else:
       model_surplus = 0

    return color_surplus + size_surplus + model_surplus

You can also use cached_property instead of property to not recompute the value each time you call it