0

I am trying to creating a python dataclass where fields can have formatted values. If I have the following

@dataclass(kw_only=True)
class childtestProduct(testProduct):

  t1:float 
  t2:str

how can I have for example t1 be like 99.90€ and t2 be like 10/8/2002 ?

N Miaoulis
  • 83
  • 8
  • What are you trying to achieve? Say `t1` is `99.9` (float), when do you want it to become `"99.90€"` (string)? – fsimonjetz Aug 10 '22 at 19:14
  • what would be input of the datalclass? – rv.kvetch Aug 10 '22 at 19:18
  • whenever I set for example t1=24.9872 to return either 24.98 or with 24.98€ – N Miaoulis Aug 10 '22 at 19:18
  • 1
    so for ex would a user would pass in `99.9€` or a float 7.893217854? also would a string be pass in to date value so ilke 02/08/10 for example? – rv.kvetch Aug 10 '22 at 19:19
  • 1
    a float of 7.8909080 and for t2 maybe the value is initialy 1/10/22 and become 1/10/2022 – N Miaoulis Aug 10 '22 at 19:20
  • 1
    You could probably do this because Python will let you do almost anything. However, for your case, you might want to consider a regular class and use the `@property` decorator so that setting the instance attributes programmatically defines your attributes the way you want. With properties, you can even store a true value and return a formatted value with the attribute getter. – philosofool Aug 10 '22 at 19:24
  • You can't format a float. You *can* have a `str`-valued property that computes its value based on the value of `t1`, though. Likewise for `t2`, though `t2` itself could be a property whose setter just formats it on assignment. – chepner Aug 10 '22 at 19:24
  • I reckon you're looking for something like this: [Dataclasses and property decorator](https://stackoverflow.com/questions/51079503/dataclasses-and-property-decorator) – fsimonjetz Aug 10 '22 at 19:26

1 Answers1

5

You could use an approach with @property decorator, as mentioned, and turn all dataclass fields into field properties:

from dataclasses import dataclass
from datetime import datetime, date


@dataclass(kw_only=True)
class ChildTestProduct:
    t1: float
    t2: datetime | str

    @property
    def t1(self) -> str:
        return f'{self._t1:.2f}€'

    @t1.setter
    def t1(self, value: float):
        self._t1 = value

    @property
    def t2(self) -> str:
        return self._t2.strftime('%m/%d/%Y')

    @t2.setter
    def t2(self, value: datetime | str):
        if isinstance(value, str):
            self._t2 = datetime.strptime(value, '%m/%d/%y').date()
        else:
            self._t2 = value


p = ChildTestProduct(t1=99.9, t2='10/8/22')
print(p)  # ChildTestProduct(t1='99.90€', t2='10/08/2022')

assert isinstance(p._t1, float)
assert isinstance(p._t2, date)
rv.kvetch
  • 9,940
  • 3
  • 24
  • 53
  • Maybe this is irrelevant in this case, but apparently implementing custom getters/setters with the same name as a field causes a bug that when you don't pass a value, it won't complain and set the value to the property object, see https://stackoverflow.com/questions/69845745/required-positional-arguments-with-dataclass-properties – fsimonjetz Aug 10 '22 at 19:38
  • 1
    @fsimonjetz Yes, this is entirely accurate, if in the above example you would try to create an object like `ChildTestProduct()`, the getters/setters would fail because dataclasses would pass in the `@property` object as the default value. The most straightforward resolution I think would be to use a metaclass or similar solution such as one I added in [this gist](https://gist.github.com/rnag/d14b05680094b871935a9a30d43d5c0d), that will correctly set the `__post_init__()` so that default values are then passed in as expected. – rv.kvetch Aug 11 '22 at 13:45