__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 AttributeError instead 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

Example · python
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: True

When 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.

Example · python
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.

Example · python
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.

Example · python
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

  • Be the first to comment on this lesson.