Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use the __post_init__ method in Dataclasses in Python

I am trying to get my hands dirty with dataclasses in Python and what i want to do is have a computed field inside my class and also add the sort_index field to the call but would also want to make it frozen so that i cannot modify any attributes of this class after definition. Below is my code:

from dataclasses import dataclass, field

def _get_year_of_birth(age: int, current_year: int=2019):
    return current_year - age

@dataclass(order=True, frozen=True)
class Person():
    sort_index: int = field(init=False, repr=False)
    name: str
    lastname: str
    age: int
    birthyear: int = field(init=False)


    def __post_init__(self):
        self.sort_index = self.age
        self.birthyear = _get_year_of_birth(self.age)



if __name__ == "__main__":
    persons = [
    Person(name="Jack", lastname="Ryan", age=35),
    Person(name="Jason", lastname="Bourne", age=45),
    Person(name="James", lastname="Bond", age=60)
    ]

    sorted_persons = sorted(persons)
    for person in sorted_persons:
        print(f"{person.name} and {person.age} and year of birth is : {person.birthyear}")

It seems that i cannot set a custom sort field inside my class and also cannot create any attribute which is computed from other attributes since i am using frozen . When i run the above i get the below error:

Traceback (most recent call last):
  File "dataclasses_2.py", line 30, in <module>
    Person(name="Jack", lastname="Ryan", age=35),
  File "<string>", line 5, in __init__
  File "dataclasses_2.py", line 23, in __post_init__
    self.sort_index = self.age
  File "<string>", line 3, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'sort_index'

Is there a better way of doing this ? Please help

like image 562
Subhayan Bhattacharya Avatar asked Mar 15 '26 09:03

Subhayan Bhattacharya


1 Answers

The problem is you are trying to set a field of a frozen object. There are two options here. First option would be to remove frozen=True from the dataclass specification. Secondly, if you still want to freeze Person instances, then you should initialize fields with method __setattr__. Therefore, your post_init method will become:

def __post_init__(self):
    object.__setattr__(self, 'sort_index', self.age)
    object.__setattr__(self, 'birthyear', _get_year_of_birth(self.age)
like image 122
magomar Avatar answered Mar 18 '26 00:03

magomar