datarekha
Python Medium Asked at GoogleAsked at MetaAsked at StripeAsked at Palantir

What does `__slots__` do in Python, and when should you use it?

The short answer

`__slots__` replaces the per-instance `__dict__` with a fixed-size C array of slot descriptors, cutting memory usage per instance by 40–60% and speeding up attribute access. Use it for classes that create many small, fixed-attribute instances — but be aware it prevents dynamic attribute assignment and complicates multiple inheritance.

How to think about it

This one shows up at companies working at scale — streaming pipelines, graph algorithms, anything spawning millions of small objects. The interviewer wants to know you understand why an ordinary instance is expensive, and what __slots__ trades away to fix it.

Every normal Python instance carries a __dict__ — a per-instance hash map that stores its attributes, so you can add new ones at any time. That flexibility isn’t free: the dict costs roughly a hundred-plus bytes per instance even when nearly empty. Across millions of small objects — graph nodes, events, sensor readings — that overhead dominates your memory. __slots__ tells Python to drop the __dict__ and store the named attributes in a compact, fixed C-level layout instead.

A worked example

import sys

class RegularPoint:
    def __init__(self, x, y):
        self.x = x
        self.y = y

class SlottedPoint:
    __slots__ = ("x", "y")
    def __init__(self, x, y):
        self.x = x
        self.y = y

rp = RegularPoint(1.0, 2.0)
sp = SlottedPoint(1.0, 2.0)

rp_size = sys.getsizeof(rp) + sys.getsizeof(rp.__dict__)   # instance + its dict
sp_size = sys.getsizeof(sp)                                # no __dict__ to add
per = rp_size - sp_size

print(f"RegularPoint: {rp_size} bytes (instance + __dict__)")
print(f"SlottedPoint: {sp_size} bytes")
print(f"Saving per instance: {per} bytes")
print(f"Saving at 1M instances: ~{per * 1_000_000 / 1e6:.0f} MB")

# Slots forbid attributes you didn't declare
try:
    sp.z = 3.0
except AttributeError as e:
    print("AttributeError:", e)

# A regular instance accepts new attributes freely
rp.z = 3.0
print("RegularPoint.z:", rp.z)
RegularPoint: 152 bytes (instance + __dict__)
SlottedPoint: 48 bytes
Saving per instance: 104 bytes
Saving at 1M instances: ~104 MB
AttributeError: 'SlottedPoint' object has no attribute 'z'
RegularPoint.z: 3.0

The slotted instance is roughly a third the size, and at a million instances that 104 bytes apiece adds up to about 104 MB saved. The cost sits right below it: sp.z = 3.0 raises, because slots fix the attribute set at class-definition time, while the regular instance happily takes a brand-new z.

Rules and caveats

  • A subclass that doesn’t redeclare __slots__ quietly regains a __dict__ — and the saving is gone.
  • To allow extra ad-hoc attributes alongside the slots, add "__dict__" to __slots__ explicitly.
  • Weak references break unless you include "__weakref__" in __slots__.
  • On Python 3.10+, @dataclass(slots=True) gives you slots without writing them by hand — the cleaner choice for new code:
from dataclasses import dataclass

@dataclass(slots=True)
class Point:
    x: float
    y: float

When to use it

Reach for __slots__ when you’re creating tens of thousands or more instances of a class with a fixed, known set of attributes — graph nodes, rows in an in-memory store, events in a stream. Below that scale, the memory win isn’t worth the lost flexibility.

Learn it properly Classes & Instances

Keep practising

All Python questions

Explore further

Skip to content