Before dataclasses, creating a simple data container in Python required writing `__init__`, `__repr__`, and `__eq__` manually — a lot of repetitive code. The `@dataclass` decorator (Python 3.7+) generates all of this automatically.
```python
from dataclasses import dataclass, field
```
## The problem without dataclasses
```python
# Old way: lots of boilerplate
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __repr__(self):
return f"Point(x={self.x}, y={self.y})"
def __eq__(self, other):
return isinstance(other, Point) and self.x == other.x and self.y == other.y
```
## The dataclass solution
```python
@dataclass
class Point:
x: float
y: float
p1 = Point(1.0, 2.0)
p2 = Point(1.0, 2.0)
print(p1) # Point(x=1.0, y=2.0) — __repr__ auto-generated
print(p1 == p2) # True — __eq__ auto-generated
print(p1.x) # 1.0
```
The decorator generates `__init__`, `__repr__`, and `__eq__` based on the class annotations.
## Default values
```python
@dataclass
class Config:
host: str = "localhost"
port: int = 8080
debug: bool = False
c = Config() # all defaults
print(c) # Config(host='localhost', port=8080, debug=False)
c2 = Config(host="0.0.0.0", port=443)
print(c2) # Config(host='0.0.0.0', port=443, debug=False)
```
**Rule**: fields with defaults must come after fields without defaults (same rule as function parameters).
## field() for mutable defaults
You cannot use `default=[]` directly — Python would share the same list between all instances. Use `field(default_factory=list)` instead:
```python
@dataclass
class Student:
name: str
grades: list = field(default_factory=list) # each instance gets its own list
active: bool = True
s1 = Student("Alice")
s2 = Student("Bob")
s1.grades.append(95)
print(s1.grades) # [95]
print(s2.grades) # [] — independent!
```
## __post_init__: computed fields and validation
```python
@dataclass
class Rectangle:
width: float
height: float
area: float = field(init=False) # excluded from __init__
def __post_init__(self):
if self.width <= 0 or self.height <= 0:
raise ValueError("Width and height must be positive")
self.area = self.width * self.height
r = Rectangle(4.0, 5.0)
print(r) # Rectangle(width=4.0, height=5.0, area=20.0)
print(r.area) # 20.0
```
`__post_init__` runs automatically after `__init__`.
Create a dataclass `Book` with fields: `title: str`, `author: str`, `year: int`, `rating: float = 0.0`. Then create two instances: `Book("1984", "Orwell", 1949, 9.5)` and `Book("Dune", "Herbert", 1965)`. Print both and verify that `==` works (create two identical Book objects and check they are equal).
Create a dataclass `Team` with fields `name: str` and `members: list` where `members` defaults to an empty list. Show that each instance gets its own independent list by creating two teams, adding a member to the first, and verifying the second is still empty. Use `field(default_factory=list)`.
from dataclasses import dataclass, field
@dataclass
class Team:
name: str
members: list = ??? # fix this
t1 = Team("Alpha")
t2 = Team("Beta")
t1.members.append("Alice")
print(t1.members) # ["Alice"]
print(t2.members) # [] <- must be empty
Solution
from dataclasses import dataclass, field
@dataclass
class Team:
name: str
members: list = field(default_factory=list)
t1 = Team("Alpha")
t2 = Team("Beta")
t1.members.append("Alice")
print(t1.members) # ["Alice"]
print(t2.members) # []
Create a dataclass `Temperature` with a single field `celsius: float`. In `__post_init__`, validate that the temperature is above absolute zero (-273.15°C) and raise `ValueError` with message `"Temperature below absolute zero"` if not. Test both valid and invalid cases.
from dataclasses import dataclass
@dataclass
class Temperature:
celsius: float
def __post_init__(self):
pass # validate here
print(Temperature(100)) # Temperature(celsius=100)
print(Temperature(-273)) # Temperature(celsius=-273)
Temperature(-300) # raises ValueError
Solution
from dataclasses import dataclass
@dataclass
class Temperature:
celsius: float
def __post_init__(self):
if self.celsius < -273.15:
raise ValueError("Temperature below absolute zero")
print(Temperature(100)) # Temperature(celsius=100)
print(Temperature(-273)) # Temperature(celsius=-273)
try:
Temperature(-300)
except ValueError as e:
print(e) # Temperature below absolute zero
Create a dataclass `Circle` with field `radius: float` and a computed field `area: float` that is NOT part of `__init__` (use `field(init=False)`). Calculate `area` in `__post_init__` as `math.pi * radius ** 2`. Print a Circle and show that `area` is included in the repr.
from dataclasses import dataclass, field
import math
@dataclass
class Circle:
radius: float
area: float = field(init=False)
def __post_init__(self):
pass # compute area here
c = Circle(5)
print(c) # Circle(radius=5, area=78.539...)
print(c.area) # 78.539...
Solution
from dataclasses import dataclass, field
import math
@dataclass
class Circle:
radius: float
area: float = field(init=False)
def __post_init__(self):
self.area = math.pi * self.radius ** 2
c = Circle(5)
print(c) # Circle(radius=5, area=78.53981633974483)
print(c.area) # 78.53981633974483
Create a frozen dataclass `Coordinate` with fields `lat: float` and `lon: float`. Demonstrate that:
1. You cannot modify a `Coordinate` instance (show the error)
2. `Coordinate` instances can be used as dictionary keys — create a dict mapping coordinates to location names.
from dataclasses import dataclass
@dataclass(frozen=???)
class Coordinate:
lat: float
lon: float
c = Coordinate(50.45, 30.52)
# 1. Try to modify (should raise FrozenInstanceError)
try:
c.lat = 0
except Exception as e:
print(e)
# 2. Use as dict key
locations = {c: "Kyiv"}
print(locations[Coordinate(50.45, 30.52)]) # Kyiv
Solution
from dataclasses import dataclass
@dataclass(frozen=True)
class Coordinate:
lat: float
lon: float
c = Coordinate(50.45, 30.52)
try:
c.lat = 0
except Exception as e:
print(type(e).__name__, e) # FrozenInstanceError
locations = {c: "Kyiv"}
print(locations[Coordinate(50.45, 30.52)]) # Kyiv
Create a dataclass `Student` with `order=True` and fields: `grade: float`, `name: str`. Create a list of 5 students with different grades and names, sort it (the primary sort should be by `grade`), and print the result. Note: ordering compares fields in declaration order — put `grade` first.
from dataclasses import dataclass
@dataclass(order=???)
class Student:
grade: float
name: str
students = [
Student(3.5, "Alice"),
Student(4.0, "Bob"),
Student(3.5, "Charlie"),
Student(2.8, "Dave"),
Student(4.0, "Eve"),
]
print(sorted(students))
Solution
from dataclasses import dataclass
@dataclass(order=True)
class Student:
grade: float
name: str
students = [
Student(3.5, "Alice"),
Student(4.0, "Bob"),
Student(3.5, "Charlie"),
Student(2.8, "Dave"),
Student(4.0, "Eve"),
]
for s in sorted(students):
print(s)
# Student(grade=2.8, name="Dave")
# Student(grade=3.5, name="Alice")
# Student(grade=3.5, name="Charlie")
# Student(grade=4.0, name="Bob")
# Student(grade=4.0, name="Eve")
Create two dataclasses: `Address` (fields: `street: str`, `city: str`, `country: str = "Ukraine"`) and `Person` (fields: `name: str`, `age: int`, `address: Address`). Create a `Person` instance with a nested `Address`, print it, and access the nested city. Then use `asdict()` to convert the person to a dictionary.
from dataclasses import dataclass, asdict
@dataclass
class Address:
street: str
city: str
country: str = "Ukraine"
@dataclass
class Person:
name: str
age: int
address: Address
p = Person("Alice", 30, Address("Main St", "Kyiv"))
print(p)
print(p.address.city)
print(asdict(p))
Create a frozen dataclass `Config` with fields `host: str = "localhost"`, `port: int = 8080`, `debug: bool = False`. Use `dataclasses.replace()` to create a production config from a development config by changing `host` and `debug`. Show that the original is unchanged and the new instance has the updated values.
from dataclasses import dataclass, replace
@dataclass(frozen=True)
class Config:
host: str = "localhost"
port: int = 8080
debug: bool = False
dev = Config(debug=True)
prod = replace(dev, ???) # change host and debug
print(dev)
print(prod)
print(dev is prod) # False
Solution
from dataclasses import dataclass, replace
@dataclass(frozen=True)
class Config:
host: str = "localhost"
port: int = 8080
debug: bool = False
dev = Config(debug=True)
prod = replace(dev, host="prod.example.com", debug=False)
print(dev) # Config(host="localhost", port=8080, debug=True)
print(prod) # Config(host="prod.example.com", port=8080, debug=False)
print(dev is prod) # False — different objects
Create a dataclass `Product` with fields `name: str`, `price: float`, `in_stock: bool = True`, and `tags: list = field(default_factory=list)`. Create 3 product instances, convert each to a dict using `asdict()`, and then serialize the list of dicts to a JSON string using `json.dumps()`. Print the result.
from dataclasses import dataclass, field, asdict
import json
@dataclass
class Product:
name: str
price: float
in_stock: bool = True
tags: list = field(default_factory=list)
products = [
Product("Apple", 0.99, tags=["fruit", "fresh"]),
Product("Laptop", 999.0, tags=["electronics"]),
Product("Pen", 1.5, in_stock=False),
]
# Convert to JSON and print
Solution
from dataclasses import dataclass, field, asdict
import json
@dataclass
class Product:
name: str
price: float
in_stock: bool = True
tags: list = field(default_factory=list)
products = [
Product("Apple", 0.99, tags=["fruit", "fresh"]),
Product("Laptop", 999.0, tags=["electronics"]),
Product("Pen", 1.5, in_stock=False),
]
data = [asdict(p) for p in products]
print(json.dumps(data, indent=2))
Design a small inventory system using dataclasses:
- `Category` (frozen, fields: `name: str`, `tax_rate: float = 0.0`)
- `Item` (fields: `name: str`, `price: float`, `quantity: int`, `category: Category`)
- `Inventory` (field: `items: list = field(default_factory=list)`, method `add_item(item)`, method `total_value()` that returns sum of price*quantity for all items)
Create a few items and print the total inventory value.
from dataclasses import dataclass, field
@dataclass(frozen=True)
class Category:
name: str
tax_rate: float = 0.0
@dataclass
class Item:
name: str
price: float
quantity: int
category: Category
@dataclass
class Inventory:
items: list = field(default_factory=list)
def add_item(self, item):
pass
def total_value(self):
pass
inv = Inventory()
food = Category("Food", 0.05)
tech = Category("Tech", 0.2)
inv.add_item(Item("Apple", 1.0, 100, food))
inv.add_item(Item("Laptop", 800.0, 5, tech))
print(inv.total_value()) # 4100.0
Solution
from dataclasses import dataclass, field
@dataclass(frozen=True)
class Category:
name: str
tax_rate: float = 0.0
@dataclass
class Item:
name: str
price: float
quantity: int
category: Category
@dataclass
class Inventory:
items: list = field(default_factory=list)
def add_item(self, item):
self.items.append(item)
def total_value(self):
return sum(item.price * item.quantity for item in self.items)
inv = Inventory()
food = Category("Food", 0.05)
tech = Category("Tech", 0.2)
inv.add_item(Item("Apple", 1.0, 100, food))
inv.add_item(Item("Laptop", 800.0, 5, tech))
print(inv.total_value()) # 4100.0
No split tab
Cookie preferences
We use necessary cookies to run the site. With your permission, we can also save your site preferences and use analytics and advertising cookies to understand usage and support the project.
Open tools in tabs.Exercises, IDE tools, and trainers stay available as site tabs.
Switch without losing context.Move between explanations, code, and utilities while keeping your place.
Use the sidebar as your map.The left panels hold navigation, settings, files, libraries, and tool controls.
PythonJavaScriptSQLite
One IDE, three practical modes
Python in the browser.Run small scripts, try libraries, and practice API requests without installing anything.
JavaScript for quick experiments.Test browser-friendly code and compare ideas next to your learning materials.
SQLite for data practice.Open the database explorer to inspect tables, write queries, and learn SQL workflows locally.
TopicIDE
Work side by side with split tabs
Keep instructions visible.Open an exercise or reference page beside the IDE instead of jumping back and forth.
Compare tools while you learn.Place regex checks, explanations, and code experiments next to each other when the task needs it.
Close the split when you are done.The workspace returns to a single focused tab, and your open site tabs remain available.
Code Typing Trainer
Or plain text
This trainer is designed for a physical keyboard.Open this section on a laptop or desktop with a wide screen. Touch typing practice will not work correctly on a phone.
Speed: 0 chars/min
0 words/min
Best speed (60s): 0 chars/min
0 words/min
Errors: 0
Total time: 0.0 s
To practice touch typing, avoid looking at your physical keyboard.