GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||||||||||||||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||||||||||||||||||
GeezerVison is showing zetcode.com — Python dataclass - using the dataclass decorator Python dataclass decoratorlast modified October 30, 2026 Python 3.15
In this article we show how to use the The dataclass decorator
The @dataclass
class Test:
...
@dataclass()
class Test:
...
@dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False,
frozen=False, match_args=True, kw_only=False, slots=False,
weakref_slot=False)
class Test:
...
These three declarations are equivalent. If no parameters are given, the defaults are used.
A regular classIn a regular class, we write the constructor and other special methods manually. regular_class.py
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
def __repr__(self):
return f'Person(name={self.name!r}, age={self.age!r})'
p1 = Person('John Doe', 34)
p2 = Person('John Doe', 34)
print(p1)
print(p1 == p2)
The $ python regular_class.py Person(name='John Doe', age=34) False A simple dataclass
The following example rewrites the class with the simple_dataclass.py
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
p1 = Person('John Doe', 34)
p2 = Person('John Doe', 34)
p3 = Person(name='Roger Roe', age=41)
print(p1)
print(p3)
print(p1 == p2)
print(p1 == p3)
The class has two fields: @dataclass
class Person:
name: str
age: int
The decorator generates print(p1 == p2)
The generated $ python simple_dataclass.py Person(name='John Doe', age=34) Person(name='Roger Roe', age=41) True False Default valuesFields can have default values. default_values.py
from dataclasses import dataclass
@dataclass
class Person:
name: str = 'unknown'
age: int = 0
p1 = Person('John Doe', 34)
print(p1)
p2 = Person()
print(p2)
p3 = Person(age=21)
print(p3)
With the assignment operator, we give the fields default values. When we do not pass the values to the constructor, the defaults are used. $ python default_values.py Person(name='John Doe', age=34) Person(name='unknown', age=0) Person(name='unknown', age=21) Field orderLike function parameters, fields without a default value cannot follow fields with a default value. field_order.py
from dataclasses import dataclass
try:
@dataclass
class Person:
name: str = 'unknown'
age: int
except TypeError as e:
print(e)
The error is raised when the class is created, because the generated
$ python field_order.py non-default argument 'age' follows default argument 'name' Mutable default values
A mutable default value such as a list would be shared by all instances.
Dataclasses reject such defaults; we use mutable_default.py
from dataclasses import dataclass, field
try:
@dataclass
class Post:
title: str
tags: list[str] = []
except ValueError as e:
print(e)
@dataclass
class Post:
title: str
tags: list[str] = field(default_factory=list)
p1 = Post('First post')
p2 = Post('Second post')
p1.tags.append('python')
print(p1)
print(p2)
The first class definition fails with a $ python mutable_default.py mutable default <class 'list'> for field tags is not allowed: use default_factory Post(title='First post', tags=['python']) Post(title='Second post', tags=[]) Frozen dataclasses
If the frozen.py
from dataclasses import dataclass, FrozenInstanceError, replace
@dataclass(frozen=True)
class Point:
x: int
y: int
p = Point(3, 4)
try:
p.x = 10
except FrozenInstanceError as e:
print(e)
p2 = replace(p, x=10)
print(p, p2)
distances = {Point(0, 0): 0, p: 5}
print(distances[Point(3, 4)])
Assigning to a field raises distances = {Point(0, 0): 0, p: 5}
With $ python frozen.py cannot assign to field 'x' Point(x=3, y=4) Point(x=10, y=4) 5 Ordering
With ordering.py
from dataclasses import dataclass
@dataclass(order=True)
class Version:
major: int
minor: int
patch: int
versions = [Version(3, 14, 2), Version(3, 9, 20), Version(3, 15, 0),
Version(3, 14, 0)]
for version in sorted(versions):
print(version)
print(Version(3, 9, 20) < Version(3, 14, 0))
print(max(versions))
Version numbers compare correctly: 3.9.20 is lower than 3.14.0, which would
not be the case if we compared them as strings. Since the instances are
comparable, we can use $ python ordering.py Version(major=3, minor=9, patch=20) Version(major=3, minor=14, patch=0) Version(major=3, minor=14, patch=2) Version(major=3, minor=15, patch=0) True Version(major=3, minor=15, patch=0) Excluding fields from comparisons
The sort_index.py
from dataclasses import dataclass, field
@dataclass(order=True)
class Task:
priority: int
name: str = field(compare=False)
tasks = [Task(3, 'write tests'), Task(1, 'fix bug'), Task(2, 'review'),
Task(1, 'deploy')]
for task in sorted(tasks):
print(task)
print(Task(1, 'a') == Task(1, 'b'))
The tasks are sorted only by their priority. Python's sort is stable, so tasks with the same priority keep their original order. Since the name is not compared, two tasks with the same priority are equal. $ python sort_index.py Task(priority=1, name='fix bug') Task(priority=1, name='deploy') Task(priority=2, name='review') Task(priority=3, name='write tests') True The asdict and astuple functions
The as_dict_fun.py
from dataclasses import dataclass, asdict, astuple
import json
@dataclass
class Address:
city: str
country: str
@dataclass
class Person:
name: str
occupation: str
age: int
address: Address
p = Person('John Doe', 'gardener', 34, Address('Bratislava', 'Slovakia'))
print(p)
print(asdict(p))
print(astuple(p))
print(json.dumps(asdict(p), indent=2))
Both functions work recursively: the nested $ python as_dict_fun.py
Person(name='John Doe', occupation='gardener', age=34, address=Address(city='Bratislava', country='Slovakia'))
{'name': 'John Doe', 'occupation': 'gardener', 'age': 34, 'address': {'city': 'Bratislava', 'country': 'Slovakia'}}
('John Doe', 'gardener', 34, ('Bratislava', 'Slovakia'))
{
"name": "John Doe",
"occupation": "gardener",
"age": 34,
"address": {
"city": "Bratislava",
"country": "Slovakia"
}
}
The field function
With the fields_fun.py
from dataclasses import dataclass, field
@dataclass
class Person:
name: str
age: int
occupation: str = field(init=False, repr=False)
p = Person('John Doe', 34)
print(p)
p.occupation = 'gardener'
print(f'{p.name} is a {p.occupation}')
In the example, we have an additional occupation: str = field(init=False, repr=False)
The $ python fields_fun.py Person(name='John Doe', age=34) John Doe is a gardener The __post_init__ method
The generated post_init.py
from dataclasses import dataclass, field
@dataclass
class Rectangle:
width: float
height: float
area: float = field(init=False)
def __post_init__(self):
if self.width <= 0 or self.height <= 0:
raise ValueError('sides must be positive')
self.area = self.width * self.height
r = Rectangle(3, 4.5)
print(r)
try:
Rectangle(-1, 2)
except ValueError as e:
print(f'error: {e}')
The $ python post_init.py Rectangle(width=3, height=4.5, area=13.5) error: sides must be positive Keyword-only fieldsKeyword-only fields must be passed by name. This makes calls with many parameters easier to read. kw_only.py
from dataclasses import dataclass, KW_ONLY
@dataclass(kw_only=True)
class Connection:
host: str
port: int = 5432
timeout: float = 10.0
@dataclass
class Message:
text: str
_: KW_ONLY
urgent: bool = False
retries: int = 3
print(Connection(host='localhost', timeout=2.5))
print(Message('hello', urgent=True))
try:
Connection('localhost', 5432)
except TypeError as e:
print(e)
With $ python kw_only.py Connection(host='localhost', port=5432, timeout=2.5) Message(text='hello', urgent=True, retries=3) Connection.__init__() takes 1 positional argument but 3 were given Slots
With slots.py
from dataclasses import dataclass
import sys
@dataclass
class Point:
x: int
y: int
@dataclass(slots=True)
class SlotPoint:
x: int
y: int
p = Point(1, 2)
sp = SlotPoint(1, 2)
print(hasattr(p, '__dict__'), hasattr(sp, '__dict__'))
print(SlotPoint.__slots__)
print(sys.getsizeof(p) + sys.getsizeof(p.__dict__), sys.getsizeof(sp))
p.z = 3
try:
sp.z = 3
except AttributeError as e:
print(e)
The regular point has a $ python slots.py
True False
('x', 'y')
344 48
'SlotPoint' object has no attribute 'z' and no __dict__ for setting new attributes
InheritanceA dataclass can inherit from another dataclass. The fields of the base class come first. inheritance.py
from dataclasses import dataclass, fields
@dataclass
class Person:
name: str
age: int
@dataclass
class Employee(Person):
salary: int
department: str = 'sales'
e = Employee('John Doe', 34, 3100)
print(e)
print(isinstance(e, Person))
print([f.name for f in fields(e)])
The $ python inheritance.py Employee(name='John Doe', age=34, salary=3100, department='sales') True ['name', 'age', 'salary', 'department'] Type annotations are not enforcedThe annotations define the fields, but they are not checked at runtime. no_validation.py
from dataclasses import dataclass
@dataclass
class Person:
name: str
age: int
p = Person(34, 'John Doe')
print(p)
print(Person.__annotations__)
We create a person with the arguments swapped, and no error is raised. Types
can be checked statically with a tool such as mypy, or validated in
$ python no_validation.py
Person(name=34, age='John Doe')
{'name': <class 'str'>, 'age': <class 'int'>}
Dataclass with pattern matching
The next example uses a dataclass with the points.py
from dataclasses import dataclass
@dataclass
class Point:
x: int
y: int
def check(p):
match p:
case Point(x=0, y=0):
print('Origin')
case Point(x, y) if y == 0:
print(f'{p} on x axis')
case Point(x, y) if x == 0:
print(f'{p} on y axis')
case Point(x, y) if x > 0 and y > 0:
print(f'{p} in Q I')
case Point(x, y) if x < 0 and y > 0:
print(f'{p} in Q II')
case Point(x, y) if x < 0 and y < 0:
print(f'{p} in Q III')
case Point():
print(f'{p} in Q IV')
case _:
print('Not a point')
points = [Point(3, 0), Point(0, 0), Point(-4, -5), Point(-4, 0), Point(0, 5),
Point(4, 8), Point(-5, 3), Point(6, -4), (1, 2)]
for p in points:
check(p)
print(Point.__match_args__)
We have a list of case Point(x=0, y=0):
print('Origin')
In this case arm, we match a point which has x=0 and y=0 coordinates. case Point(x, y) if x > 0 and y > 0:
print(f'{p} in Q I')
Positional patterns such as $ python points.py
Point(x=3, y=0) on x axis
Origin
Point(x=-4, y=-5) in Q III
Point(x=-4, y=0) on x axis
Point(x=0, y=5) on y axis
Point(x=4, y=8) in Q I
Point(x=-5, y=3) in Q II
Point(x=6, y=-4) in Q IV
Not a point
('x', 'y')
Sourcedataclasses - Data Classes - Python documentation In this article we have worked with Python dataclass decorators. AuthorMy name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming. List all Python tutorials. | ||||||||||||||||||||||
| You are visitor 01402520 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |