Dataclasses example

Fields

In this post we will discuss how to modify certain properties of the attributes of DataClass object, without explicitly writing code for it using field function.

field() function – dataclasses.field(*, default=MISSING, default_factory=MISSING, repr=True, hash=None, init=True, compare=True, metadata=None)

from dataclasses import dataclass, field 
  
@dataclass
class GfgArticle: 
    title: str
    author: str
    language: str = field(default ='Python3') 
    upvotes: int = 0
        
# A DataClass Object
article = GfgArticle("DataClass", "vibhu4agarwal") 
article

Out:
GfgArticle(title='DataClass', author='vibhu4agarwal', language='Python3', upvotes=0)

=========================================================
"""
default_factory : If provided, it must be a zero-argument callable that will be called when a default value is needed for this field. The return value of the callable will be set as the default value for the attribute in object creation.
"""

from dataclasses import dataclass, field 
from random import choice 
  
def get_default_language(): 
    languages = ['Python3', 'Java', "CPP"] 
    return choice(languages) 
  
@dataclass
class GfgArticle: 
    title: str
    author: str
    language: str = field(default_factory = get_default_language) 
    upvotes: int = 0
 
article = GfgArticle("DataClass", "vibhu4agarwal") 
print(article)

Out:
GfgArticle(title='DataClass', author='vibhu4agarwal', language='Python3', upvotes=0)

The above code puts one of the Python3, Java or CPP as default value for language while DataClass object creation. The init, repr and hash parameters are similar to that in the dataclass function as discussed in previous article. compare parameter can be related to order as that in dataclass function. The difference is being in their ability to be applicable only to a particular attribute, not to all the attributes in the DataClass under the decorator.

  • init : If true (the default), this field is included as a parameter to the generated init() method. A way to set default value should be provided when init is set to False.

  • repr : If true (the default), this field is included in the string returned by the generated repr() method.

  • compare : If true (the default), this field is included in the generated equality and comparison methods (eq(), gt(), et al.)

from dataclasses import dataclass, field 
  
@dataclass
class GfgArticle: 
  
    title: str = field(compare = False) 
    author: str = field(repr = False) 
    language: str = field(default ='Python3') 
    upvotes: int = field(init = False, default = 0) 
  
# DataClass objects 
# Note the difference in their title value 
article1 = GfgArticle("DataClass", "vibhu4agarwal") 
article2 = GfgArticle("Python Packaging", "vibhu4agarwal") 
  
print(article1) 
article1.author
# print(article1 == article2)

Out:
GfgArticle(title='DataClass', language='Python3', upvotes=0)
'vibhu4agarwal'
  • hash : This can be a bool or None. If true, this field is included in the generated hash() method. If None (the default), use the value of compare: this would normally be the expected behavior.

A field should be considered in the hash if it’s used for comparisons. Setting this value to anything other than None is discouraged.

  • metadata : This is usually a dictionary, the key-value pair indicating various information and it’s data.

In the script, it’s value can be accessed by querying dataclass_fields variable of the object.

from dataclasses import dataclass, field 
  
@dataclass
class GfgArticle: 
  
    title: str = field(compare = False) 
    author: str = field(metadata ={'data': 'Profile Handle'}) 
    language: str = field(default ='Python3') 
    upvotes: int = field(init = False, default = 0) 
  
# A DataClass object 
article = GfgArticle("DataClass", "vibhu4agarwal") 
print(article) 
print(article.__dataclass_fields__['author'].metadata)

Out:

GfgArticle(title='DataClass', author='vibhu4agarwal', language='Python3', upvotes=0)
{'data': 'Profile Handle'}

Inheritance


from dataclasses import dataclass

@dataclass
class Article:
    title:str
    content:str
    author:str

@dataclass
class GfgArticle(Article):
    language:str
    author:str
    upvotes:int=0

Few points from above code:

  1. Article is subclassed by GfgArticle

  2. Both SuperClass and SubClass are DataClasses – although super-class or sub-class being a normal class is also possible. When a DataClass inherits a normal class, the init() from the super-class is overidden in sub-class.

  3. author in GfgArticle overrides the same in Article – As the basic concept of inheritance, the value for its assignment is first looked in the sub-class and followed up the tree in super-class.

Behaviour of init() of GfgArticle: If init() is not explicitly provided, the default init() expects attributes of super-class (Article) followed by attributes of sub-class as parameters.

If init() is explicitly provided, it should somehow initialize all it’s own attributes as well as those in the super-class (Article).

from dataclasses import dataclass 
  
@dataclass
class Article: 
    title: str
    content: str
    author: str
  
  
@dataclass(init = False) 
class GfgArticle(Article): 
  
    language: str
    author: str
    upvotes: int = 0
  
    def __init__(self, title): 
        self.title = title 
        self.content = "Inheritance Concepts"
        self.author = "vibhu4agarwal"
        self.language = "Python3"
        
  
  
dClassObj = GfgArticle("DataClass") 
dClassObj

Out:
GfgArticle(title='DataClass',content='InheritanceConcepts',author='vibhu4agarwal', language='Python3', upvotes=0)

Post-init

we will discuss how to modify values of some attributes during object creation without coding it in init() by using post-init processing.

__post_init__(): This function when made, is called by in-built init() after initialization of all the attributes of DataClass. Basically, object creation of DataClass starts with init() (constructor-calling) and ends with postinit__() (post-init processing).

This feature is very handy at times when certain attributes are dependent on the parameters passed in the init() but do not get their values directly from them. That is, they get their values after performing some operation on a subset of arguments received in the constructor.

Continuing the same example we’ve been seeing in this series of articles, suppose there is an attribute called author_name which gets its value from the profile handle to name mapping in the defined dictionary name. So author_name is dependent on profile handle which author attribute receives, so using post_init() should be an ideal choice this case.

from dataclasses import dataclass, field 
  
name_data = {'Terrain': 'Jim','Protoss':'Probe','Zerg':'Queen'} 
  
@dataclass
class GfgArticle: 
  
    title : str
    language: str
    fake_name: str
    real_name: str = field(init = False) 
    upvotes: int = 0
  
    def __post_init__(self): 
        self.real_name = name_data.get(self.fake_name,'Sorry, Not found')
        

t1 = GfgArticle("Learn Python",'FR','Terrain')
t1
Out:GfgArticle(title='Learn Python', language='FR', fake_name='Terrain', real_name='Jim', upvotes=0)

t2 = GfgArticle("Learn Python",'FR','Apple')
t2

Out[18]:
GfgArticle(title='Learn Python', language='FR', fake_name='Apple', real_name='Sorry, Not found', upvotes=0)

interconversion to and from other datatypes

we will discuss how to get values of a DataClass object into a dictionary or tuple pairs and how to create a DataClass in a different way – from values, instead of defining it directly.

asdict() function –:dataclasses.asdict(instance, *, dict_factory=dict)

One can simply obtain an attribute to value pair mappings in form of a dictionary by using this function, passing the DataClass object to the instance parameter of the function.

astuple() function –:dataclasses.astuple(instance, *, tuple_factory=tuple)

Similar to asdict, one can simply obtain ordered values of dataclass attributes in the form of a tuple using this function by passing the DataClass object to the instance parameter of the function.

from dataclasses import dataclass,field
from dataclasses import asdict,astuple 
  
@dataclass
class GfgArticle: 
  
    title : str
    language: str
    author: str
    upvotes: int = field(default = 0) 
  
  
dClassObj = GfgArticle("DataClass", 
                       "Python3", 
                       "vibhu4agarwal") 
  
dictArticle = asdict(dClassObj) 
tupleArticle = astuple(dClassObj) 
  
print(dictArticle) 
print(tupleArticle)

Out:
{'title': 'DataClass', 'language': 'Python3', 'author': 'vibhu4agarwal', 'upvotes': 0}
('DataClass', 'Python3', 'vibhu4agarwal', 0)

make_dataclass()

from dataclasses import make_dataclass, field 
  
GfgArticle = make_dataclass( 
    'GfgArticle', 
    [ 
        ('title', str), 
        ('language', str), 
        ('author', str), 
        ('upvotes', int, field(default = 0)) 
    ] 
) 
  
dClassObj = GfgArticle("DataClass", 
                       "Python3", 
                       "vibhu4agarwal") 
dClassObj

Out:
GfgArticle(title='DataClass', language='Python3', author='vibhu4agarwal', upvotes=0)

is_dataclass()


from dataclasses import is_dataclass 
  
print(is_dataclass(dictArticle)) 
print(is_dataclass(tupleArticle))

Out: 
False
False

is_dataclass(dClassObj)
Out: True

is_dataclass(GfgArticle)
Out: True

Last updated