Python Data Class

Python Data Class

Part of what classes are used for in Python is to represent data. It is by using the __init__ function that we can accept data into an object as it is created, and assign those data values to the object using the self keyword. You might notice that sometimes this code setup has a lot of boilerplate to it. In other words, you have to write the same structure of code to simply store simple values on an object. It can seem verbose sometimes. This is where Python Data Classes come in. Beginning in Python version 3.7, you can use a new syntax to create classes for holding data in your application and that is what we will examine in this tutorial.


Data Class Definition

Recall the usual way we define a class in Python using __init__ and super().

class Book():
    def __init__(self, title, author, price):
        super().__init__()
        self.title = title
        self.author = author
        self.price = price

    def __str__(self):
        return f'{self.title} by {self.author}, costs {self.price}'

    def __repr__(self):
        return f'title={self.title},author={self.author},price={self.price}'


book1 = Book('Python Crash Course', 'Eric Matthes', 23.99)
book2 = Book('Serious Python', 'Julien Danjou', 25.43)

# print each object
print(book1)
print(book2)

# use str() and repr()
print(str(book1))
print(repr(book2))
Python Crash Course by Eric Matthes, costs 23.99
Serious Python by Julien Danjou, costs 25.43
Python Crash Course by Eric Matthes, costs 23.99
title=Serious Python,author=Julien Danjou,price=25.43

Now let’s see it using the Data Class approach. To use dataclasses, you must import them using from dataclasses import dataclass. We then need to decorate the class with the @dataclass decorator. The code below has less boilerplate, yet we still get the output we expect.

from dataclasses import dataclass


@dataclass
class Book():
    title: str
    author: str
    price: float

    def __str__(self):
        return f'{self.title} by {self.author}, costs {self.price}'

    def __repr__(self):
        return f'title={self.title},author={self.author},price={self.price}'


book1 = Book('Python Crash Course', 'Eric Matthes', 23.99)
book2 = Book('Serious Python', 'Julien Danjou', 25.43)

# print each object
print(book1)
print(book2)

# use str() and repr()
print(str(book1))
print(repr(book2))
Python Crash Course by Eric Matthes, costs 23.99
Serious Python by Julien Danjou, costs 25.43
Python Crash Course by Eric Matthes, costs 23.99
title=Serious Python,author=Julien Danjou,price=25.43

Post Initialization

If you decide to use Data Classes, you have access to a __post_init__ method that can perform additional object initialization since the data class automatically handled the init function like magic. The data class decorator provides a special function named __post_init__ that you can override and that is called after the built-in init function has finished. Here is an example of that in action.

from dataclasses import dataclass


@dataclass
class Book:
    title: str
    author: str
    pages: int
    price: float

    # the __post_init__ function lets us customize additional properties
    # after the object has been initialized via built-in __init__
    def __post_init__(self):
        self.description = f'{self.title} by {self.author}, {self.pages} pages'


# create some Book objects
book1 = Book('Python Crash Course', 'Eric Matthes', 544, 23.99)
book2 = Book('Serious Python', 'Julien Danjou', 225, 25.43)

# use the description attribute
print(book1.description)
print(book2.description)
Python Crash Course by Eric Matthes, 544 pages
Serious Python by Julien Danjou, 225 pages

Default Values

Part of using classes in Python is making use of the ability to set default values on objects at run time. It seems this might be missing with data classes since the data class handles the __init__ method automagically for you. You can still make use of default values, but the syntax is a bit different. Here is an example.

from dataclasses import dataclass, field


@dataclass
class Book:
    # you can define default values when attributes are declared
    title: str = 'Empty Book'
    author: str = 'Your Imagination'
    pages: int = 0
    price: float = 0.0


# Create a default book object
book1 = Book()
print(book1)

# Create a specified book, price is set by field operator
book1 = Book('Python Crash Course', 'Eric Matthes', 544, 23.99)
book2 = Book('Serious Python', 'Julien Danjou', 225, 25.43)
print(book1)
print(book2)
Book(title='Empty Book', author='Your Imagination', pages=0, price=0.0)
Book(title='Python Crash Course', author='Eric Matthes', pages=544, price=23.99)
Book(title='Serious Python', author='Julien Danjou', pages=225, price=25.43)

Immutable Data Class

The last thing we’ll look at in this data class tutorial is how to make objects that are immutable. This is done by simply passing frozen=True to the @dataclass decorator.

from dataclasses import dataclass


@dataclass(frozen=True)
class Book:
    title: str
    author: str
    pages: int
    price: float

    # You can define methods in a dataclass like any other
    def bookinfo(self):
        return f'{self.title}, by {self.author}'


# create some instances
book1 = Book('Python Crash Course', 'Eric Matthes', 544, 23.99)
book2 = Book('Serious Python', 'Julien Danjou', 225, 25.43)

# access fields
print(book1.title)
print(book2.author)

# print the book itself - dataclasses provide a default
# implementation of the __repr__ function
print(book1)

# comparing two dataclasses
book3 = Book('Automate the Boring Stuff with Python', 'Al Sweigart ', 592, 26.99)
print(book1 == book3)

# change some fields, call a regular class method
book1.title = 'Python for Kids'
book1.pages = 864
print(book1.bookinfo())
Python Crash Course
Julien Danjou
Book(title='Python Crash Course', author='Eric Matthes', pages=544, price=23.99)
False
Traceback (most recent call last):
  File "C:/python/OOP/dataclass.py", line 33, in 
    book1.title = 'Python for Kids'
  File "", line 3, in __setattr__
dataclasses.FrozenInstanceError: cannot assign to field 'title'