What is `__slots__`, and what problem does it solve?
Quick Answer
By default, every instance gets a `__dict__` to store arbitrary attributes, which costs memory per instance. Declaring `__slots__ = ("x", "y")` tells Python to allocate fixed storage for exactly those attribute names instead of a per-instance `dict`, cutting memory use significantly for classes with many instances, at the cost of losing dynamic attribute assignment (and needing extra care with multiple inheritance).
Detailed Answer
The default cost: a __dict__ per instance
class Point:
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
p.__dict__ # {'x': 1, 'y': 2}
p.z = 3 # works -- __dict__ is dynamic, any attribute can be added
Every instance carries its own dict for attribute storage, which is
flexible but has real memory overhead (~100+ bytes per instance just for
the dict, on top of the attributes themselves) — significant if you create
millions of small objects.
__slots__: fixed, dict-free storage
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
p.z = 3 # AttributeError: 'Point' object has no attribute 'z'
p.__dict__ # AttributeError: no __dict__ at all
With __slots__, Python allocates fixed-size storage for exactly the
named attributes (implemented as descriptors under the hood), eliminating
the per-instance dict entirely. For a class with millions of instances
(e.g., points in a large dataset, nodes in a graph), this can cut memory
use by 40-50%+ and speed up attribute access slightly too.
The tradeoffs
- No dynamic attributes — assigning an attribute not in
__slots__raisesAttributeError. This is often a feature (catches typos early) but breaks code that relies on ad-hoc attribute assignment. - No default
__dict__or__weakref__unless explicitly included in__slots__— if you need weak references to instances, add"__weakref__"to the slots tuple. - Multiple inheritance is tricky — at most one base class in the MRO
can have non-empty
__slots__with actual layout conflicts; combining multiple non-empty-__slots__bases raisesTypeError: multiple bases have instance lay-out conflict. - Every subclass needs its own
__slots__— if a subclass doesn't declare__slots__, it gets a__dict__anyway, silently undoing the memory savings for that subclass's instances.
When to use it
Reach for __slots__ on classes you'll instantiate in large numbers (data
records, tree/graph nodes, simple value objects) where memory footprint
matters — not as a default for every class, since it removes flexibility
for comparatively small classes where the memory savings don't matter.
Interview-ready summary: __slots__ trades away per-instance dict
flexibility for fixed, lower-memory attribute storage — a meaningful win
when instantiating a class millions of times, but something to opt into
deliberately (and consistently across a whole inheritance chain), not a
default.