Signup/Sign In

Python Dataclass - Part 2

Posted in Programming   LAST UPDATED: SEPTEMBER 18, 2021

    In our previous post covering introduction to Python dataclass, we understood the basics of a dataclass and its advantages over normal classes. In this post, we will dive deeper into dataclasses and understand various parameters that can be used with dataclasses.

    When we use the dataclass decorator, we can provide the following attributes, with each being used for including some special function:

    1. init parameter

    2. repr parameter

    3. eq parameter

    4. order parameter

    5. unsafe_hash parameter

    6. frozen parameter

    Following is the syntax for it:

    @dataclasses.dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)

    Let us cover every parameter that has been mentioned in the above dataclass.


    1. The init parameter

    When the init parameter is set to True, it indicates that a default constructor has to be added in the dataclass. It behaves in the same way as an __init__ method would behave if it were explicitly defined in the class. If the init value is set to False, a default constructor won't be created.

    Time for an example:

    from dataclasses import dataclass
    
    @dataclass(init = False)
    class data_class():
        title: str
        value : int
    
    
    #Creating a dataclass object 
    class_instance = data_class("Studytonight",
                         "12")

    Output:

    TypeError: data_class() takes no arguments

    Why is this the output?

    That is because the init parameter has been set to False as a result the default constructor hasn't been created which would have initialized the class instance with values specified.

    On the other hand, if the value of init is specified as True in the above code, the output would be:

    data_class(title='Studytonight', value='12')


    2. The repr parameter

    When the repr parameter is set to True, it represents the data present in the dataclass in a neat and readable manner. It behaves in the same way as the __repr__ method(internally this method is added to the class) would behave it if were explicitly defined. If it were set to False, it outputs the hash value of the object in memory.

    Time for an example:

    from dataclasses import dataclass
    
    @dataclass(repr = True)
    class data_class():
        title: str
        value : int
    
    #Creating a dataclass object 
    class_instance = data_class("Studytonight",
                         "12")
    print(class_instance)

    Output:

    data_class(title='Studytonight', value='12')

    If the repr parameter to the dataclass decorator was set to False, it would output the below line:

    <__main__.data_class object at 0x0000020D33B4CA90>


    3. The eq parameter

    If the eq parameter in the dataclass decorator is set to True, it compares the contents of the two instances (of the same class using == or !=) and returns True or False depending on whether or not the contents are same. It behaves in the same way as an __eq__ method would behave it if were explicitly defined.

    Time for an example:

    from dataclasses import dataclass
    
    @dataclass(eq = True)
    class data_class():
        title: str
        value : int
    
    #Creating a dataclass object 
    class_instance_1 = data_class("Studytonight",
                         "12")
    class_instance_2 = data_class("Studytonight",
                         "12")
    
    check_for_equality = class_instance_1==class_instance_2
    check_for_inequality = class_instance_1!=class_instance_2
    print(check_for_equality)
    print(check_for_inequality)

    Output:

    True
    False

    Since the eq parameter has been set to True, the contents of the instances are compared.

    If the eq parameter was set to False, the output would be:

    False
    True

    This is because their hash representation (based on the location at which the values of instances are present) are compared and obviously the two instances are stored in two different memory locations, hence the result would be False.


    4. The order parameter

    The order parameter comprises of >, <, <=, and >= operators. When the order parameter is set to True, the attributes of every instance is compared to the other instance in question.

    Time for an example:

    from dataclasses import dataclass
    
    @dataclass(order = True)
    class data_class():
        title: str
        value : int
    
    #Creating a dataclass object 
    class_instance_1 = data_class("Studytonight",
                         "13")
    class_instance_2 = data_class("Studytonight",
                         "12")
    greater_checking = class_instance_1 > class_instance_2
    lesser_or_equal_checking = class_instance_1 <= class_instance_2
    
    print(greater_checking)
    print(lesser_or_equal_checking)

    Output:

    True
    False

    When the order parameter is set to False, a TypeError is shown.

    Traceback (most recent call last):
      File "<ipython-input-20-258d272e70dd>", line 14, in <module>
        greater_checking = class_instance_1>class_instance_2
    TypeError: '>' not supported between instances of 'data_class' and 'data_class'
    
    

    When we specify the order parameter as True, then the special methods __lt__(for less than), __gt__(for greater than) etc are added to the dataclass by default.


    5. The unsafe_hash parameter

    The unsafe_hash parameter refers to granting permission for the generation of a hash value for mutable objects. In reality, the mutable objects (objects whose values can be changed) can't have a hash representation. This means, even by using the hash method, mutable object's hash values shouldn't be generated.

    But upon setting the unsafe_hash parameter as True, it generates a hash value for objects of the dataclass (which is mutable). As the parameter name itself suggests, it is unsafe to generate a hash representation for a mutable object.

    Internally, this adds the __hash()__ method to the the dataclass.

    Time for an example:

    from dataclasses import dataclass
    
    @dataclass(unsafe_hash = False )
    class data_class():
        title: str
        value : int
    
    #Creating a dataclass object 
    class_instance_1 = data_class("Studytonight",
                         "14")
    print(class_instance_1) 
    print(hash(class_instance_1))

    Output:

    data_class(title='Studytonight', value='14')
    
    Traceback (most recent call last):
      File "<ipython-input-28-97bf774fdaac>", line 13, in <module>
        print(hash(class_instance_1))
    TypeError: unhashable type: 'data_class'

    When the parameter unsafe_hash is set to True, the output is:

    data_class(title='Studytonight', value='14')
    1769436267547038324

    Every time the values of the attributes in the dataclass is changed, the hash representation of it changes when unsafe_hash is set to True.

    Now your question might be, what is the point of an object being immutable?

    Well, a mutable object can be changed to become immutable by setting the frozen parameter to True (the frozen parameter has been explained in the below section). This way, when the unsafe_hash parameter is set to True, a hash representation is generated for an immutable object.

    On the other hand, if the frozen is set to False and unsafe_hash is True, the dataclass must be used extremely carefully since it will have the ability to generate a hash value for a mutable object.

    Also, if the eq parameter is set as True and frozen parameter is also set as True then a __hash__ method is added to the dataclass by default.


    6. The frozen parameter

    If this paramter is set to True, once the values of an object are set or initialized, their values can't be changed. The values freeze. This works on the same lines of const keyword in C++ and final keyword in Java. When this parameter is set to True, once a value is defined, it can't be changed. If any efforts are made to change the value, a FrozenInstanceError is displayed.

    Time for an example:

    from dataclasses import dataclass
    
    @dataclass(frozen = True )
    class data_class():
        title: str
        value : int
    
    #Creating a dataclass object 
    class_instance_1 = data_class("Studytonight",
                         "13")
    print(class_instance_1)  #Before reassigning value
    class_instance_1. value = 12
    print(class_instance_1)  #After reassigning a new value

    Output:

    data_class(title='Studytonight', value='13')
    
    Traceback (most recent call last):
      File "<ipython-input-21-be74e685c8ae>", line 13, in <module>
        class_instance_1. value = 12
      File "<string>", line 3, in __setattr__
    FrozenInstanceError: cannot assign to field 'value'

    When the frozen parameter is set to False, the dataclass behaves normally, and the values of the class can be changed. The above code (when frozen = False) gives below output:

    data_class(title='Studytonight', value='13')
    data_class(title='Studytonight', value=12)


    Conclusion

    In this post, we understood how various parameters supplied to the dataclass change the working of various methods inside the dataclass. In the next post, we will learn about the field function of dataclass in Python. So stay tuned.

    You may also like:

    About the author:
    I love writing about Python and have more than 5 years of professional experience in Python development. I like sharing about various standard libraries in Python and other Python Modules.
    Tags:PythonPython Dataclass
    IF YOU LIKE IT, THEN SHARE IT
     

    RELATED POSTS