šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.
HTML MASTER CLASS /// LEARN TAGS /// BUILD STRUCTURE /// SEMANTIC WEB /// HTML MASTER CLASS /// LEARN TAGS ///

Python __slots__

Cut memory usage and lock down attribute creation with __slots__ — understand what it trades away and exactly when that trade is worth making.

⚔ Total XP: 0|šŸ’» python XP: 0

Skill Matrix

UNLOCK NODES BY LEARNING NEW TAGS.

System Hub

Core logic.

Quick Quiz //

What does __slots__ = ("x", "y") remove from instances of that class?


šŸš€ LEVEL UP TO SENIOR:Unlock 500+ Advanced Practical Challenges & Exercises.
šŸŽ“ COURSERA PARTNER:Earn professional Google, Meta, and IBM certificates to supercharge your resume.

Every ordinary Python object carries a hidden __dict__ for storing its instance attributes, which is flexible but not free — at scale, that per-instance dict is a meaningful memory cost. __slots__ tells Python to skip it, storing attributes in fixed, pre-declared slots instead. This lesson covers the real trade-offs, not just the syntax.

1The Hidden Cost of a Per-Instance __dict__

An ordinary Python class instance stores its attributes in a regular dictionary, instance.__dict__, created automatically the first time you access it (or earlier, depending on implementation details) and attached to every single instance. Dictionaries in CPython are fast and flexible — average O(1) attribute access, and you can add or remove attributes freely at any time — but that flexibility has a real, measurable memory cost: an empty dict alone typically takes more memory than the two float fields it might end up holding for a simple Point.

At small scale — a few dozen objects — this overhead is irrelevant. At scale — millions of parsed rows, graph nodes, or simulation particles — the cumulative cost of one dict per instance becomes the dominant driver of your process's memory footprint, sometimes by a wide margin over the actual data being stored.

__slots__ = ('x', 'y') tells CPython, at class-definition time, exactly which attribute names this class will ever use. Instead of a dict, the interpreter allocates fixed-size storage (conceptually similar to a C struct) sized precisely for those named slots — eliminating the dict's overhead entirely for every instance of the class.

āœ•
—
+
class PointNoSlots:
    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

p = PointNoSlots(1.0, 2.0)
print(p.__dict__)  # {'x': 1.0, 'y': 2.0} — a real dict per instance
localhost:3000
Memory Behavior
__slots__ instance
No per-instance dict; fixed-size, pre-allocated slots

2The Trade-Off: What You Give Up

The memory savings are not free. The most immediate consequence is that you can no longer set an attribute that wasn't declared in __slots__ — p.z = 3.0 on a PointSlots instance raises AttributeError rather than silently creating a new attribute. Many teams treat this as a feature rather than a cost, since it catches typos (p.xx = 1.0 instead of p.x = 1.0) and prevents accidental scope creep in a class's shape — but it does mean you must plan the class's full attribute set up front.

Multiple inheritance with __slots__ gets genuinely tricky: you can't have more than one base class with non-empty __slots__ unless their slot layouts don't conflict, and mixing a slotted class with a non-slotted one reintroduces a __dict__ anyway, silently forfeiting the memory savings you were trying to get. This is also true, less obviously, for subclasses: a subclass that itself doesn't declare __slots__ gets a __dict__ automatically, since Python has to support whatever new attributes that subclass's code might add — meaning every subclass in the hierarchy has to opt in explicitly for the optimization to hold all the way down.

Weak references (weakref) also don't work by default on slotted classes unless you explicitly add '__weakref__' to __slots__, since the weakref machinery itself normally piggybacks on the instance __dict__.

āœ•
—
+
class PointSlots:
    __slots__ = ('x', 'y')

    def __init__(self, x: float, y: float):
        self.x = x
        self.y = y

p = PointSlots(1.0, 2.0)
print(p.x, p.y)   # 1.0 2.0
# p.__dict__ would raise AttributeError — there isn't one
localhost:3000
Error Behavior
p.z = 3.0
AttributeError — undeclared attributes are rejected, not silently added

3When the Trade Is Actually Worth Making

__slots__ earns its complexity specifically when you're instantiating large numbers of small, structurally-fixed objects — parsed records, graph/tree nodes, simulation entities, geometric points — where the per-instance dict overhead, multiplied across millions of instances, becomes a measurable fraction of total memory usage. Profiling first (with sys.getsizeof, tracemalloc, or a memory profiler) rather than guessing is the professional default; adding __slots__ to a class that's only ever instantiated a handful of times is complexity with no measurable payoff.

For data-holding classes specifically, it's worth noting that @dataclass(slots=True) — available since 3.10 — generates the __slots__ declaration automatically from your annotated fields, combining the boilerplate reduction of dataclasses with the memory benefit of slots in one decorator argument, without you needing to keep a separate __slots__ tuple in sync with the field list by hand.

As a rule of thumb: reach for __slots__ (or @dataclass(slots=True)) when profiling has shown per-instance memory is a real bottleneck for a class you're creating in bulk, and skip it everywhere else — it's an optimization with real behavioral trade-offs, not a default you should apply to every class out of habit.

āœ•
—
+
p = PointSlots(1.0, 2.0)
p.z = 3.0
# AttributeError: 'PointSlots' object has no attribute 'z'
localhost:3000
Modern Shortcut
@dataclass(slots=True)
Generates __slots__ automatically from annotated fields (3.10+)

4Step-by-Step Breakdown

Creating a million small objects and watching memory usage explode? __slots__ is often the fix — but it changes how the class behaves, not just how much RAM it uses.

By default, every instance gets its own __dict__ to hold attributes — flexible, but it costs memory per object, even for a class with just two fixed fields.

__slots__ declares the exact attribute names allowed, and Python stores them in fixed slots instead of a per-instance dict.

Checkpoint: What does __slots__ = ("x", "y") remove from instances of that class?

  • →The per-instance __dict__ used to store arbitrary attributes
  • →The ability to define any methods on the class

That restriction is a feature, not just a side effect: assigning an undeclared attribute raises AttributeError immediately, catching typos and accidental new fields.

Inheritance needs care: a subclass without its own __slots__ silently gets a __dict__ back, undoing the memory savings of the parent class.

Checkpoint: If ColoredPoint(PointSlots) forgets to declare its own __slots__, what happens?

  • →ColoredPoint instances silently get a __dict__ back, losing the memory benefit
  • →Python raises an error at class-definition time

That closes out Modern Python. Next, we go deeper into the language's metaprogramming and iteration machinery with Advanced Python — starting with Decorators.

Remove a Real Instance __dict__. Finish check: __slots__ replaces the per-instance __dict__ with fixed storage.

Level Up šŸš€

Advanced cheat sheets, SEO tricks, and interview prep for this topic.

Browser Support

ChromeSupported

Fully supported (via server-side Python execution).

FirefoxSupported

Fully supported (via server-side Python execution).

SafariSupported

Fully supported (via server-side Python execution).

EdgeSupported

Fully supported (via server-side Python execution).

Best Practices

Profile before optimizing with __slots__

Memory savings only matter when a class is instantiated at real scale. Measure with tracemalloc or sys.getsizeof first; don't add __slots__ to every class by default.

Prefer @dataclass(slots=True) over hand-written __slots__ for data classes

It keeps the slots list automatically in sync with the annotated fields, eliminating an easy-to-forget manual maintenance step whenever a field is added or removed.

Frequent Bugs

THE BUG

Adding __slots__ to a base class but forgetting to declare __slots__ (even as an empty tuple) on every subclass, silently losing the memory optimization down the hierarchy.

THE FIX

Declare __slots__ explicitly on every class in the hierarchy that should stay slotted, even if a particular subclass adds no new attributes (__slots__ = () is valid and keeps the chain slotted).

Real-World Examples

Memory-Efficient Graph Nodes for a Large Dataset

A graph-processing tool needs to hold millions of lightweight node objects in memory simultaneously to run a traversal algorithm, where per-instance memory overhead directly limits the maximum graph size that fits in RAM.

class GraphNode:
    __slots__ = ('id', 'neighbors', 'visited')

    def __init__(self, node_id: int):
        self.id = node_id
        self.neighbors: list[int] = []
        self.visited = False

Interview Prep

?Frequently Asked Questions

Pascual Vila

Pascual Vila

Frontend Instructor // Code Syllabus

Common Pitfalls & Errors

The Error //

Subclassing a slotted base class without declaring __slots__ on the subclass, silently reintroducing a per-instance __dict__ and losing the memory optimization without any error being raised.

# Wrong: silently reintroduces __dict__, no error class Base: __slots__ = ('x',) class Sub(Base): def __init__(self, x, y): self.x = x self.y = y # works, but only because __dict__ came back # Correct: stays slotted all the way down class Sub(Base): __slots__ = ('y',) def __init__(self, x, y): self.x = x self.y = y

The Solution //

Declare __slots__ = (...) — or __slots__ = () if adding no new fields — on every subclass in the hierarchy that must stay slotted.

Lesson Glossary

[01]__slots__

A class attribute declaring the fixed set of instance attribute names allowed, replacing the per-instance __dict__ with pre-allocated storage.

Code Preview
// __slots__ context

[02]__dict__ (instance)

The dictionary Python normally attaches to each object instance to store its attributes dynamically; __slots__ removes this per instance.

Code Preview
// __dict__ (instance) context

[03]Instance lay-out conflict

A TypeError raised when attempting multiple inheritance from more than one base class that each define non-empty, incompatible __slots__.

Code Preview
// Instance lay-out conflict context

[04]@dataclass(slots=True)

A dataclass decorator option (3.10+) that auto-generates a __slots__ declaration from the class's annotated fields.

Code Preview
// @dataclass(slots=True) context

Continue Learning