__slots__ — Leaner Objects
Declare a fixed set of attributes to cut memory use and catch typo-attributes early.
By default every Python instance stores its attributes in a per-instance __dict__ — flexible, but memory-hungry when you have millions of objects. Declaring __slots__ tells Python the exact attributes an instance will have, so it can skip the dictionary entirely.
class Point:
__slots__ = ('x', 'y') # no per-instance __dict__
def __init__(self, x, y):
self.x, self.y = x, y
p = Point(1, 2)
print(p.x, p.y) # 1 2
# p.z = 3 -> AttributeError: 'Point' object has no attribute 'z'Two real benefits
- Memory — slotted instances can use dramatically less RAM. At scale (data pipelines, simulations, ORMs) this is the difference between fitting in memory and not.
- Typo safety — assigning an attribute you never declared raises
AttributeErrorinstead of silently creating a misspelled field.
The trade-offs
Slotted classes cannot gain new attributes at runtime and, without care, do not play well with multiple inheritance or __dict__-based tricks. Use slots on small, numerous, well-defined objects — not on everything.
Example
import sys
from dataclasses import dataclass
# Same class, with and without slots — compare footprint and safety
class Loose:
def __init__(self, x, y):
self.x, self.y = x, y
@dataclass(slots=True)
class Tight:
x: int
y: int
loose, tight = Loose(1, 2), Tight(1, 2)
# Slotted instances have no __dict__ at all
print('loose has __dict__:', hasattr(loose, '__dict__')) # True
print('tight has __dict__:', hasattr(tight, '__dict__')) # False
# Typos are caught instead of silently added
loose.nmae = 'oops' # silently created — a lurking bug
print('loose typo stuck :', loose.nmae)
try:
tight.nmae = 'oops' # rejected immediately
except AttributeError as e:
print('tight rejects :', str(e).split(' object')[0], '...')
# A rough footprint hint (exact numbers vary by build)
print('slots save memory per instance:', True)
# Output:
# loose has __dict__: True
# tight has __dict__: False
# loose typo stuck : oops
# tight rejects : 'Tight' ...
# slots save memory per instance: TrueWhen to use it
- A high-frequency trading system uses __slots__ on a Tick class to cut memory for millions of instances.
- A data model uses __slots__ to catch typos in attribute names at assignment time rather than silently creating new ones.
- A game engine's Particle class defines __slots__ to avoid the per-instance __dict__ overhead.
More examples
Define __slots__
Restricts the instance to exactly the declared slots; assigning an undeclared attribute raises AttributeError.
class Point:
__slots__ = ('x', 'y')
def __init__(self, x, y): self.x, self.y = x, y
p = Point(3, 4)
print(p.x, p.y) # 3 4
# p.z = 0 -> AttributeError: 'Point' has no attribute 'z'Memory comparison
Shows that __slots__ eliminates the instance __dict__, reducing memory for large numbers of objects.
import sys
class WithDict:
def __init__(self, x, y): self.x, self.y = x, y
class WithSlots:
__slots__ = ('x', 'y')
def __init__(self, x, y): self.x, self.y = x, y
a = WithDict(1, 2)
b = WithSlots(1, 2)
print(sys.getsizeof(a.__dict__)) # ~200 bytes
print(hasattr(b, '__dict__')) # False__slots__ in a subclass
Demonstrates __slots__ inheritance: each class declares only its own new attributes.
class Base:
__slots__ = ('x',)
def __init__(self, x): self.x = x
class Child(Base):
__slots__ = ('y',) # adds y, inherits x
def __init__(self, x, y):
super().__init__(x)
self.y = y
c = Child(1, 2)
print(c.x, c.y) # 1 2
Discussion