slots stores attributes in fixed slots instead of a per-instance dict
By default, each instance of a class gets a dict, a hash table that maps attribute names to values. That is flexible but expensive: the dict itself has overhead, and every instance carries it even if the class has two attributes. Defining slots replaces the per-instance dict with a fixed set of C-level descriptors, one per slot, so an instance stores only the declared attributes in pre-allocated offsets. The savings are significant for classes created in large numbers: often 40 to 60 percent less memory per instance. The costs are real too: you cannot add arbitrary attributes, slots do not participate in dict-based tooling like vars(), and multiple inheritance with non-empty slots is restricted to one base with non-empty slots and the rest with empty slots. Choose slots for data-heavy value objects created in bulk, and avoid them when you need dynamic attributes or heavy introspection.
Slots eliminate the per-instance dict, saving memory and slightly speeding up attribute access.
Declare slots as a tuple of names to save a few bytes over a list.
Include weakref and dict explicitly in slots if you need weak references or dynamic attributes.
dataclasses support slots=True since 3.10, which is the cleanest way to get both.
Trade-off: slots remove flexibility. Dynamic attributes, monkey-patching, and some serialization libraries break.
Common mistake: defining slots in a subclass while the parent still has dict, which keeps the dict and negates most savings.
Common mistake: assuming slots makes instances immutable. It does not; assignment to declared slots still works.
Version note: dataclass slots=True arrived in 3.10. The basic slot semantics have been stable since Python 2.2.
0-2 years experience
2-5 years experience
5-8 years experience
8+ years experience