By default, every Python object stores its attributes in a dictionary (__dict__). This is flexible — we can add any attribute at any time — but it costs memory. When we have millions of instances of the same class, that memory adds up fast.
__slots__ tells Python: “These are the only attributes this class will ever have.” Python then uses a more compact internal structure instead of a dict.
How It Works
class Point:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(3, 4)
print(p.x) # 3
p.z = 5 # AttributeError: 'Point' object has no attribute 'z'
No __dict__ is created. Attributes are stored in fixed-size slots, like a struct in C.
Why Use It?
Two main reasons:
- Memory savings — each instance uses significantly less memory (no per-instance dict)
- Faster attribute access — direct offset lookup instead of dict hash lookup
The savings matter when we have many instances:
import sys
class WithDict:
def __init__(self, x, y):
self.x = x
self.y = y
class WithSlots:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
a = WithDict(1, 2)
b = WithSlots(1, 2)
print(sys.getsizeof(a.__dict__)) # ~104 bytes (the dict itself)
print(sys.getsizeof(a)) # ~48 bytes (the object)
print(sys.getsizeof(b)) # ~48 bytes (no dict overhead at all)
With a million instances, the dict overhead alone can be hundreds of megabytes.
No Dynamic Attributes
The trade-off is clear: we lose the ability to add arbitrary attributes at runtime.
class User:
__slots__ = ("name", "email")
def __init__(self, name, email):
self.name = name
self.email = email
u = User("Manish", "m@example.com")
u.age = 25 # AttributeError — 'age' not in __slots__
If we need some flexibility, we can include __dict__ in our slots:
class FlexUser:
__slots__ = ("name", "__dict__") # fixed + dynamic attributes
def __init__(self, name):
self.name = name
u = FlexUser("Manish")
u.age = 25 # works — stored in __dict__
But this partially defeats the purpose.
Inheritance Caveats
Slots get tricky with inheritance:
- If a parent has
__slots__and a child doesn’t define__slots__, the child gets a__dict__(losing the benefit) - Both parent and child should define
__slots__for full savings - Don’t repeat parent slots in the child
class Base:
__slots__ = ("x",)
class Child(Base):
__slots__ = ("y",) # only new attributes here
c = Child()
c.x = 1 # from Base's slots
c.y = 2 # from Child's slots
With Dataclasses (Python 3.10+)
Dataclasses support slots natively with the slots=True parameter. This is the cleanest way to use them.
from dataclasses import dataclass
@dataclass(slots=True)
class Point:
x: float
y: float
p = Point(3.0, 4.0)
print(p.x) # 3.0
p.z = 5 # AttributeError
No need to define __slots__ manually — the dataclass decorator handles it.
When to Use slots
- Many instances of the same class (data processing, game entities, ORM rows)
- Known, fixed attributes that won’t change
- Performance-critical code where memory or speed matters
When NOT to use:
- Prototyping or small scripts (not worth the hassle)
- Classes that need dynamic attributes
- When we’re not creating many instances
In simple language, __slots__ is us telling Python “I know exactly what attributes this class needs — please be efficient about it.” We give up flexibility for speed and memory savings.