4.2. Dataclass Definition

4.2.1. SetUp

>>> from dataclasses import dataclass

4.2.2. Required Fields

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

4.2.3. Default Fields

>>> @dataclass
... class User:
...     firstname: str
...     lastname: str
...     role: str = 'admin'

4.2.4. Union Fields

>>> @dataclass
... class User:
...     firstname: str
...     lastname: str
...     age: int | float

4.2.5. Optional Fields

>>> @dataclass
... class User:
...     firstname: str
...     lastname: str
...     age: int | None

4.2.6. Literal Field

SetUp:

>>> from typing import Literal

Define class:

>>> @dataclass
... class User:
...     firstname: str
...     lastname: str
...     role: Literal['users', 'staff', 'admins']

4.2.7. ClassVar Fields

  • from typing import ClassVar

One of two places where dataclass() actually inspects the type of a field is to determine if a field is a class variable as defined in PEP 526. It does this by checking if the type of the field is typing.ClassVar. If a field is a ClassVar, it is excluded from consideration as a field and is ignored by the dataclass mechanisms. Such ClassVar pseudo-fields are not returned by the module-level fields() function.

SetUp:

>>> from typing import ClassVar

Definition:

>>> @dataclass
... class User:
...     firstname: str
...     lastname: str
...     age: int
...     AGE_MIN: ClassVar[int] = 30
...     AGE_MAX: ClassVar[int] = 50

Usage:

>>> User('Mark', 'Watney', age=42)
User(firstname='Mark', lastname='Watney', age=42)

Note, that those fields will not be displayed in repr or while printing.

4.2.8. Assignments

# %% About
# - Name: Dataclass Definition Attributes
# - Difficulty: easy
# - Lines: 4
# - Minutes: 2

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 1. Use Dataclass to define class `Point` with attributes:
#    - `x: int` with default value `0`
#    - `y: int` with default value `0`
# 2. Run doctests - all must succeed

# %% Polish
# 1. Użyj Dataclass do zdefiniowania klasy `Point` z atrybutami:
#    - `x: int` z domyślną wartością `0`
#    - `y: int` z domyślną wartością `0`
# 2. Uruchom doctesty - wszystkie muszą się powieść

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from inspect import isclass
>>> from dataclasses import is_dataclass

>>> assert isclass(Point), 'Point is not a class'
>>> assert is_dataclass(Point), 'Point is not a dataclass, add decorator'
>>> assert hasattr(Point, 'x')
>>> assert hasattr(Point, 'y')

>>> Point()
Point(x=0, y=0)

>>> Point(x=0, y=0)
Point(x=0, y=0)

>>> Point(x=1, y=2)
Point(x=1, y=2)
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% Imports
from dataclasses import dataclass

# %% Types
Point: type
x: int
y: int

# %% Data

# %% Result

# %% About
# - Name: Dataclass Definition AccessModifiers
# - Difficulty: easy
# - Lines: 2
# - Minutes: 2

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 1. Modify dataclass `User` to add attributes:
#    - Public: `firstname`, `lastname`
# 2. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj dataclass `User` aby dodać atrybuty:
#    - Publiczne: `firstname`, `lastname`
# 2. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - Public attribute name starts with a lowercase letter

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from inspect import isclass

>>> assert isclass(User)
>>> assert hasattr(User, '__annotations__')

>>> assert 'firstname' in User.__dataclass_fields__
>>> assert 'lastname' in User.__dataclass_fields__
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% Imports
from dataclasses import dataclass

# %% Types
User: type
firstname: str
lastname: str

# %% Data

# %% Result
@dataclass
class User:
    ...

# %% About
# - Name: Dataclass Definition ClassVar
# - Difficulty: easy
# - Lines: 3
# - Minutes: 2

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 1. Define class User with:
#    - instance variable `age: int` (no default value)
#    - class variable `AGE_MIN: int` with default value `18`
#    - class variable `AGE_MAX: int` with default value `65`
# 2. Use `dataclass`
# 3. Use `typing`
# 4. Run doctests - all must succeed

# %% Polish
# 1. Zdefiniuj klasę User z polami klasowymi:
#    - zmienną instancji `age: int` (bez domyślnej wartości)
#    - zmienną klasy `AGE_MIN: int` z domyślną wartością `18`
#    - zmienną klasy `AGE_MAX: int` z domyślną wartością `65`
# 2. Użyj `dataclass`
# 3. Użyj `typing`
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from inspect import isclass
>>> from dataclasses import _FIELD_CLASSVAR

>>> assert isclass(User)
>>> assert hasattr(User, '__annotations__')
>>> assert hasattr(User, '__dataclass_fields__')

>>> fields = User.__dataclass_fields__
>>> assert 'age' in fields
>>> assert 'AGE_MIN' in fields
>>> assert 'AGE_MAX' in fields

>>> assert fields['AGE_MIN']._field_type is _FIELD_CLASSVAR
>>> assert fields['AGE_MAX']._field_type is _FIELD_CLASSVAR
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% Imports
from typing import ClassVar
from dataclasses import dataclass

# %% Types
User: type
age: int
AGE_MIN: ClassVar[int]
AGE_MAX: ClassVar[int]

# %% Data

# %% Result
@dataclass
class User:
    firstname: str
    lastname: str
    ...

# %% About
# - Name: Dataclass Definition Flat
# - Difficulty: easy
# - Lines: 6
# - Minutes: 3

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 1. You received input data in JSON format from the API
# 2. Using `dataclass` model data to create class `Pet`
# 3. Run doctests - all must succeed

# %% Polish
# 1. Otrzymałeś z API dane wejściowe w formacie JSON
# 2. Wykorzystując `dataclass` zamodeluj dane aby stworzyć klasę `Pet`
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% References
# [1]: https://petstore.swagger.io/#/pet/getPetById

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from inspect import isclass
>>> from dataclasses import is_dataclass
>>> import json

>>> assert isclass(Pet)
>>> assert is_dataclass(Pet)

>>> fields = {'id', 'category', 'name', 'photoUrls', 'tags', 'status'}
>>> assert set(Pet.__dataclass_fields__.keys()) == fields, \
f'Invalid fields, your fields should be: {fields}'

>>> from typing import get_type_hints
>>> annotations = get_type_hints(Pet)
>>>
>>> assert annotations['id'] == int
>>> assert annotations['category'] == str
>>> assert annotations['name'] == str
>>> assert annotations['photoUrls'] == str
>>> assert annotations['tags'] == list[str]
>>> assert annotations['status'] == str

>>> from hashlib import sha1
>>> hex = sha1(DATA.encode()).hexdigest()
>>> assert hex == '678056047aaf56a0435073d4ca43cc493bc9288e' , \
'Do not modify the `DATA` variable'

>>> data = json.loads(DATA)
>>> result = Pet(**data)

>>> result  # doctest: +NORMALIZE_WHITESPACE
Pet(id=0, category='dogs', name='doggie', photoUrls='img/dogs/0.png',
    tags=['dog', 'hot-dog'], status='available')

"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% Imports
from dataclasses import dataclass

# %% Types
Pet: type
id: int
category: str
name: str
photoUrls: str
tags: list[str]
status: str

# %% Data
DATA = """
{
  "id": 0,
  "category": "dogs",
  "name": "doggie",
  "photoUrls": "img/dogs/0.png",
  "tags": ["dog", "hot-dog"],
  "status": "available"
}
"""

# %% Result
@dataclass
class Pet:
    ...

# %% About
# - Name: Dataclass Definition Nested
# - Difficulty: easy
# - Lines: 6
# - Minutes: 3

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% English
# 1. You received input data in JSON format from the API
# 2. Using `dataclass` model `DATA` to create class `Pet`
# 3. Run doctests - all must succeed

# %% Polish
# 1. Otrzymałeś z API dane wejściowe w formacie JSON
# 2. Wykorzystując `dataclass` zamodeluj `DATA` aby stworzyć klasę `Pet`
# 3. Uruchom doctesty - wszystkie muszą się powieść

# %% References
# [1]: https://petstore.swagger.io/#/pet/getPetById

# %% Doctests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 10), \
'Python 3.10+ required'

>>> from inspect import isclass
>>> from dataclasses import is_dataclass
>>> import json

>>> assert isclass(Pet)
>>> assert is_dataclass(Pet)

>>> fields = {'id', 'category', 'name', 'photoUrls', 'tags', 'status'}
>>> assert set(Pet.__dataclass_fields__.keys()) == fields, \
f'Invalid fields, your fields should be: {fields}'

>>> from typing import get_type_hints
>>> annotations = get_type_hints(Pet)
>>>
>>> assert annotations['id'] == int
>>> assert annotations['category'] == dict[str, int|str]
>>> assert annotations['name'] == str
>>> assert annotations['photoUrls'] == list[str]
>>> assert annotations['tags'] == list[dict[str, int|str]]
>>> assert annotations['status'] == str

>>> from hashlib import sha1
>>> hex = sha1(DATA.encode()).hexdigest()
>>> assert hex == '81adbc16cb2a172997c773784cded8e58a278abb' , \
'Do not modify the `DATA` variable'

>>> data = json.loads(DATA)
>>> result = Pet(**data)

>>> result  # doctest: +NORMALIZE_WHITESPACE
Pet(id=0, category={'id': 0, 'name': 'dogs'}, name='doggie',
    photoUrls=['img/dogs/0.png'], tags=[{'id': 0, 'name': 'dog'},
                                        {'id': 1, 'name': 'hot-dog'}],
    status='available')
"""

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% Imports
from dataclasses import dataclass

# %% Types
Pet: type
id: int
category: dict[str, int|str]
name: str
photoUrls: list[str]
tags: list[dict[str, int|str]]
status: str

# %% Data
DATA = """
{
  "id": 0,
  "category": {
    "id": 0,
    "name": "dogs"
  },
  "name": "doggie",
  "photoUrls": [
    "img/dogs/0.png"
  ],
  "tags": [
    {
      "id": 0,
      "name": "dog"
    },
    {
      "id": 1,
      "name": "hot-dog"
    }
  ],
  "status": "available"
}
"""

# %% Result
@dataclass
class Pet:
    ...