__slots__ & Memory Optimisation
debt(d7/e3/b3/t7)
Closest to 'only careful code review or runtime testing' (d7) — memory-profiler and pympler from detection_hints catch the issue only via runtime profiling, and there's no linter that flags missing __slots__ on heavily-instantiated classes.
Closest to 'simple parameterised fix' (e3) — quick_fix is adding __slots__ declaration to a class, but it's not purely one line (must enumerate attributes, ensure subclasses also declare it, possibly add __weakref__), making it slightly more than e1.
Closest to 'localised tax' (b3) — applies_to is web/cli but the choice is per-class; classes with __slots__ impose constraints (no dynamic attrs, pickle complications, subclass discipline) on that class hierarchy but don't reshape the whole codebase.
Closest to 'serious trap' (t7) — misconception and common_mistakes show multiple counterintuitive behaviors: subclasses silently regain __dict__ defeating the optimization, weakref breaks without explicit declaration, and pickle silently misbehaves — these contradict normal Python class inheritance expectations.
Also Known As
TL;DR
Explanation
By default, each Python object has a __dict__ (a dictionary) storing its attributes — this adds ~200 bytes overhead per instance and allows arbitrary attribute addition. Declaring __slots__ = ('x', 'y') creates fixed-slot descriptors instead, eliminating __dict__. Benefits: 40-60% less memory, faster attribute access (no dict lookup), prevents accidental typo attributes. Trade-offs: no dynamic attributes, no __dict__, no weakref by default (add '__weakref__' to slots). Python 3.10+ dataclasses support slots=True directly.
Common Misconception
Why It Matters
Common Mistakes
- Forgetting to add __slots__ to subclasses — a subclass without __slots__ still creates __dict__.
- Not including '__weakref__' in slots when the class needs to be weakly referenced.
- Using __slots__ on classes that need dynamic attribute assignment — slots forbid this.
- Forgetting that __slots__ makes pickle more complex — must implement __getstate__/__setstate__.
Code Examples
# No slots — each instance has a __dict__:
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
import sys
p = Point(1, 2)
print(sys.getsizeof(p)) # ~48 bytes, plus ~200 bytes for __dict__
# 1 million Points: ~248MB
# With __slots__ — fixed layout, no __dict__:
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
import sys
p = Point(1, 2)
print(sys.getsizeof(p)) # ~56 bytes, no __dict__
# 1 million Points: ~56MB — 4x less memory
# Python 3.10+ dataclass with slots:
from dataclasses import dataclass
@dataclass(slots=True)
class Point:
x: float
y: float