7.1. Dataclass About

  • Used for easier class definition

  • Since Python 3.7: PEP 557 -- Data Classes

  • This are not static fields!

  • Dataclasses require Type Annotations

SetUp:

>>> from dataclasses import dataclass

Class:

>>> class User:
...     def __init__(self, firstname, lastname):
...         self.firstname = firstname
...         self.lastname = lastname

Dataclass:

>>> @dataclass
... class User:
...     firstname: str
...     lastname: str

7.1.1. Problem

>>> class User:
...     firstname: str
...     lastname: str
...
...     def __init__(self, firstname: str, lastname: str) -> None:
...         self.firstname = firstname
...         self.lastname = lastname
...
...     def __str__(self) -> str:
...         return f'{self.firstname} {self.lastname}'
...
...     def __repr__(self):
...         clsname = self.__class__.__name__
...         firstname = self.firstname
...         lastname = self.lastname
...         return f'{clsname}({firstname=}, {lastname=})'
...
...     def __eq__(self, other):
...         return self.__class__ is other.__class__ \
...            and self.firstname == other.firstname \
...            and self.lastname == other.lastname

To add field middlename: str we need to make change in 7 different places:

>>> class User:
...     firstname: str
...     middlename: str  # 1
...     lastname: str
...
...     def __init__(self, firstname: str, middlename: str, lastname: str) -> None:  # 2
...         self.firstname = firstname
...         self.middlename = middlename  # 3
...         self.lastname = lastname
...
...     def __str__(self) -> str:
...         return f'{self.firstname} {self.middlename} {self.lastname}'  # 4
...
...     def __repr__(self):
...         clsname = self.__class__.__name__
...         firstname = self.firstname
...         middlename = self.middlename  # 5
...         lastname = self.lastname
...         return f'{clsname}({firstname=}, {middlename=}, {lastname=})'  # 6
...
...     def __eq__(self, other):
...         return (
...             self.__class__ is other.__class__ and
...             self.firstname == other.firstname and
...             self.middlename == other.middlename and  # 7
...             self.lastname == other.lastname
...         )

7.1.2. Solution

>>> @dataclass
... class User:
...     firstname: str
...     lastname: str

To add field middlename: str we need to make change in 1 place:

>>> @dataclass
... class User:
...     firstname: str
...     middlename: str  # 1
...     lastname: str

7.1.3. Use Case - 0x01

>>> from dataclasses import dataclass
>>> from datetime import date
>>> from typing import Literal, Self
>>>
>>>
>>> @dataclass
... class Group:
...     gid: int
...     name: str
>>>
>>> @dataclass
... class User:
...     firstname: str
...     lastname: str
...     email: str
...     username: str
...     password: str
...     birthday: date | None = None
...     height: int | float | None = None
...     weight: int | float | None = None
...     role: Literal['admin', 'user', 'guest'] = 'user'
...     friends: list[Self] | None = None
...     groups: list[Group] | None = None

7.1.4. Use Case - 0x02

>>> from dataclasses import dataclass
>>> from itertools import starmap
>>>
>>>
>>> DATA = [
...     ('Sepal length', 'Sepal width', 'Petal length', 'Petal width', 'Species'),
...     (5.8, 2.7, 5.1, 1.9, 'virginica'),
...     (5.1, 3.5, 1.4, 0.2, 'setosa'),
...     (5.7, 2.8, 4.1, 1.3, 'versicolor'),
...     (6.3, 2.9, 5.6, 1.8, 'virginica'),
...     (6.4, 3.2, 4.5, 1.5, 'versicolor'),
...     (4.7, 3.2, 1.3, 0.2, 'setosa'),
... ]
>>>
>>> @dataclass
... class Iris:
...     sepal_length: float
...     sepal_width: float
...     petal_length: float
...     petal_width: float
...     species: str
>>>
>>> result = starmap(Iris, DATA[1:])
>>> list(result)  
[Iris(sepal_length=5.8, sepal_width=2.7, petal_length=5.1, petal_width=1.9, species='virginica'),
 Iris(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, species='setosa'),
 Iris(sepal_length=5.7, sepal_width=2.8, petal_length=4.1, petal_width=1.3, species='versicolor'),
 Iris(sepal_length=6.3, sepal_width=2.9, petal_length=5.6, petal_width=1.8, species='virginica'),
 Iris(sepal_length=6.4, sepal_width=3.2, petal_length=4.5, petal_width=1.5, species='versicolor'),
 Iris(sepal_length=4.7, sepal_width=3.2, petal_length=1.3, petal_width=0.2, species='setosa')]