Slots

__slots__ is a class attribute that reserves memory for listed instance variables and prevents the creation of __dict__, significantly reducing memory footprint.

Basic Usage

Without __slots__, each instance has a __dict__:

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

p = Point(1, 2)
print(p.__dict__)  # {'x': 1, 'y': 2}

# With __slots__ - no __dict__
class Point:
    __slots__ = ['x', 'y']
    
    def __init__(self, x, y):
        self.x = x
        self.y = y

p = Point(1, 2)
print(p.__dict__)  # AttributeError!

Memory Savings

__slots__ can reduce memory usage by 40-50%:

import sys

class WithoutSlots:
    def __init__(self, a, b, c, d):
        self.a = a
        self.b = b
        self.c = c
        self.d = d

class WithSlots:
    __slots__ = ['a', 'b', 'c', 'd']
    
    def __init__(self, a, b, c, d):
        self.a = a
        self.b = b
        self.c = c
        self.d = d

# Create instances
without = WithoutSlots(1, 2, 3, 4)
with_slots = WithSlots(1, 2, 3, 4)

print(sys.getsizeof(without.__dict__))  # ~100+ bytes (dict)
print(sys.getsizeof(with_slots.__slots__)) # Much less!

Inheritance with Slots

Subclasses need their own __slots__:

class Base:
    __slots__ = ['x']

class Derived(Base):
    __slots__ = ['y']  # Add more slots

d = Derived()
d.x = 1
d.y = 2
# d.z = 3 would raise AttributeError

# Without __slots__ in derived - has __dict__ again
class DerivedWithDict(Base):
    pass  # No __slots__ - gets __dict__ back

Multiple Inheritance

All parent classes must have __slots__:

class A:
    __slots__ = ['a']

class B:
    __slots__ = ['b']

class C(A, B):
    __slots__ = ['c']

c = C()
c.a, c.b, c.c = 1, 2, 3

# Warning: both parents should have __slots__
# Otherwise C will have __dict__

Using Properties with Slots

Define computed properties alongside slots:

class Rectangle:
    __slots__ = ['_width', '_height']
    
    def __init__(self, width, height):
        self._width = width
        self._height = height
    
    @property
    def area(self):
        return self._width * self._height
    
    @property
    def perimeter(self):
        return 2 * (self._width + self._height)

r = Rectangle(3, 4)
print(r.area)       # 12
print(r.perimeter)   # 14
r._width = 5  # Can still modify

Weakref Support

Add __weakref__ for weak references:

import weakref

class Node:
    __slots__ = ['value', '_parent', '__weakref__']
    
    def __init__(self, value, parent=None):
        self.value = value
        self._parent = parent

n1 = Node(1)
n2 = Node(2, n1)

# Can create weak reference
ref = weakref.ref(n2)
print(ref().value)  # 2

Slots Are Not Magic

Key limitations and considerations:

# Can't add new attributes not in __slots__
class Person:
    __slots__ = ['name']

p = Person()
p.name = "Alice"
p.age = 30  # AttributeError!

# Class attributes still work
Person.motto = "Hello"  # This works
print(Person.motto)

# Need to use slots in all subclasses to maintain benefits
class Employee(Person):
    pass  # Employee has __dict__ again!

When to Use Slots

Use Case Recommendation
Many instances (10k+) Use __slots__
Few instances Not necessary
Dynamic attributes needed Don't use __slots__
Memory constrained Use __slots__
Simple data containers Use __slots__ or dataclass
Tip: dataclasses with slots=True (Python 3.10+) gives you the best of both worlds.