Python 3.7 Features: Dataclasses and More
Python 3.7 dropped in June 2018 with some fantastic features. Dataclasses alone are worth the upgrade. Let’s explore what’s new.
Dataclasses: The Headline Feature
Before dataclasses:
class Point:
def __init__(self, x, y, z=0):
self.x = x
self.y = y
self.z = z
def __repr__(self):
return f'Point(x={self.x}, y={self.y}, z={self.z})'
def __eq__(self, other):
return self.x == other.x and self.y == other.y and self.z == other.z
With dataclasses:
from dataclasses import dataclass
@dataclass
class Point:
x: float
y: float
z: float = 0
That’s it. __init__, __repr__, __eq__ are generated automatically.
Customization Options
@dataclass(frozen=True) # Immutable
class Config:
host: str
port: int = 8080
@dataclass(order=True) # Adds comparison operators
class Version:
major: int
minor: int
patch: int
Default Factories
For mutable defaults:
from dataclasses import dataclass, field
@dataclass
class ShoppingCart:
items: list = field(default_factory=list)
created_at: datetime = field(default_factory=datetime.now)
Post-Init Processing
@dataclass
class Rectangle:
width: float
height: float
area: float = field(init=False)
def __post_init__(self):
self.area = self.width * self.height
Converting to Dict/Tuple
from dataclasses import asdict, astuple
point = Point(1, 2, 3)
asdict(point) # {'x': 1, 'y': 2, 'z': 3}
astuple(point) # (1, 2, 3)
Built-in breakpoint()
Debug without importing:
# Before
import pdb; pdb.set_trace()
# Now
breakpoint()
Respects PYTHONBREAKPOINT environment variable:
PYTHONBREAKPOINT=0 python script.py # Disable
PYTHONBREAKPOINT=ipdb.set_trace python script.py # Use ipdb
Typing Improvements
Forward References as Strings
class Node:
def __init__(self, children: 'List[Node]'): # String for forward ref
self.children = children
With from __future__ import annotations:
from __future__ import annotations
class Node:
def __init__(self, children: List[Node]): # No quotes needed
self.children = children
typing.get_type_hints()
Resolve forward references at runtime:
from typing import get_type_hints
hints = get_type_hints(Node.__init__)
Dictionary Order Preserved
Now guaranteed by language spec (was CPython implementation detail in 3.6):
d = {'a': 1, 'b': 2, 'c': 3}
list(d.keys()) # Always ['a', 'b', 'c']
Context Variables
For propagating context through async code:
from contextvars import ContextVar
request_id: ContextVar[str] = ContextVar('request_id')
async def process_request():
token = request_id.set('abc123')
await do_work() # request_id is available
request_id.reset(token)
namedtuple Improvements
from typing import NamedTuple
class Point(NamedTuple):
x: float
y: float
z: float = 0.0 # Defaults now supported
Time Functions Get Nanoseconds
import time
time.time_ns() # Nanosecond timestamp
time.perf_counter_ns() # High-resolution counter
time.process_time_ns() # CPU time
Faster Module Attribute Access
Modules can now define __getattr__ and __dir__:
# mymodule.py
def __getattr__(name):
if name == 'lazy_value':
return compute_expensive_value()
raise AttributeError(f'module has no attribute {name}')
Performance Improvements
Python 3.7 is faster:
- Method calls up to 20% faster
dictoperations optimized- Startup time reduced
These are free wins—no code changes required.
When to Upgrade
Do upgrade if:
- You want dataclasses
- You’re starting new projects
- Your dependencies support 3.7
Wait if:
- Critical dependencies don’t support 3.7
- You’re in a freeze period
Migration Tips
- Test thoroughly—some behaviors changed
- Update dependencies first
- Consider
pyupgradefor automatic syntax updates - Gradually adopt new features
Dataclasses vs Alternatives
Dataclasses vs namedtuple:
- Dataclasses are mutable (unless frozen)
- Better defaults handling
- Can have methods
Dataclasses vs attrs:
- attrs has more features (validators, converters)
- Dataclasses are standard library
- Use attrs for complex cases
Dataclasses vs Pydantic:
- Pydantic validates at runtime
- Use Pydantic for APIs and settings
- Dataclasses are lighter weight
Final Thoughts
Python 3.7 is a quality release. Dataclasses alone will save you hundreds of lines of boilerplate.
The improvements are incremental but meaningful. Upgrade when you can—your code will thank you.
Less boilerplate, more productivity.