Functions & Classes
Dataclasses
3 min read
Dataclasses reduce boilerplate when creating classes that mainly store data. They automatically generate __init__, __repr__, and comparison methods.
Basic Dataclass
from dataclasses import dataclass
@dataclass
class Message:
role: str
content: str
# Automatically generates __init__
msg = Message(role="user", content="Hello!")
# Automatically generates __repr__
print(msg) # Message(role='user', content='Hello!')
# Attribute access works normally
print(msg.role) # "user"
print(msg.content) # "Hello!"
Comparison: Regular Class vs Dataclass
# Regular class (verbose)
class MessageOld:
def __init__(self, role, content):
self.role = role
self.content = content
def __repr__(self):
return f"Message(role='{self.role}', content='{self.content}')"
def __eq__(self, other):
return self.role == other.role and self.content == other.content
# Dataclass (concise)
@dataclass
class Message:
role: str
content: str
# All methods above are generated automatically!
Default Values
from dataclasses import dataclass
from typing import Optional
@dataclass
class ChatConfig:
model: str = "gpt-4"
temperature: float = 0.7
max_tokens: int = 1000
stream: bool = False
# Use defaults
config1 = ChatConfig()
# Override some values
config2 = ChatConfig(model="claude-3-opus", temperature=0.9)
Fields with Default Factory
For mutable defaults (lists, dicts), use field():
from dataclasses import dataclass, field
from typing import List, Dict
@dataclass
class Agent:
name: str
model: str = "gpt-4"
# WRONG: history: List[Dict] = [] # Shared between instances!
# RIGHT: Use field with default_factory
history: List[Dict] = field(default_factory=list)
config: Dict = field(default_factory=dict)
agent1 = Agent(name="Agent1")
agent2 = Agent(name="Agent2")
agent1.history.append({"msg": "Hello"})
print(agent1.history) # [{"msg": "Hello"}]
print(agent2.history) # [] (separate list)
Frozen Dataclasses (Immutable)
from dataclasses import dataclass
@dataclass(frozen=True)
class ApiKey:
provider: str
key: str
key = ApiKey(provider="openai", key="sk-...")
# key.provider = "anthropic" # Error! Cannot modify frozen dataclass
Practical AI Example
from dataclasses import dataclass, field
from typing import List, Optional
@dataclass
class ToolCall:
name: str
arguments: dict
@dataclass
class AgentResponse:
content: str
tool_calls: List[ToolCall] = field(default_factory=list)
finish_reason: str = "stop"
usage: Optional[dict] = None
@dataclass
class ConversationTurn:
user_message: str
assistant_response: AgentResponse
timestamp: float = field(default_factory=lambda: __import__('time').time())
# Clean, type-safe data structures
response = AgentResponse(
content="Let me search for that.",
tool_calls=[ToolCall(name="search", arguments={"query": "Python"})]
)
Next, we'll learn how to handle errors gracefully in Python. :::