__slots__

__slots__ = ['name', ...]
Returns: None (class attribute) · Updated March 28, 2026 · Dunder Methods
dunder memory classes

__slots__ is a class-level attribute that explicitly declares which instance attributes a class permits. Instead of the default per-instance dictionary that Python normally creates for every object, __slots__ switches storage to a fixed-size array with known memory offsets. The result is a significant reduction in RAM overhead per instance.

How slots Replaces dict

Without __slots__, every instance carries a __dict__ — a full hash table mapping attribute names to values. That dictionary object alone adds roughly 56 bytes of overhead on top of whatever you’re actually storing. Add in the per-attribute hash table entries and you quickly see why memory usage explodes when you instantiate millions of objects.

With __slots__, the __dict__ disappears entirely:

class Point:
    __slots__ = ['x', 'y']

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

p = Point(1, 2)
print(p.x)   # 1
print(p.y)   # 2

Attributes are now stored at fixed byte offsets inside the object structure. Each slot attribute costs approximately 8 bytes per instance instead of the 40+ bytes a dict entry would cost. For a class with four attributes instantiated 500,000 times, you’re looking at roughly 16 MB of attribute storage versus 80+ MB with dictionaries.

The speed difference is marginal. Direct offset access is technically faster than dict hashing, but in practice the performance gain rarely matters. Use __slots__ for memory, not for speed.

Preventing Arbitrary Attributes

Once you declare __slots__, you cannot assign attributes that aren’t listed:

class Point:
    __slots__ = ['x', 'y']

p = Point(1, 2)
p.z = 3   # AttributeError: 'Point' object has no attribute 'z'

This is a hard constraint. You cannot bypass it on a per-instance basis. Python raises AttributeError immediately, which means typos in attribute names surface fast rather than silently creating new fields.

Inheritance and slots

__slots__ from a parent class are inherited — child instances can access those attributes without the child needing to redeclare them. But here’s the catch: if the child does not define its own __slots__, it automatically gets a __dict__ back, which restores the memory overhead you were trying to avoid.

class Base:
    __slots__ = ['x']

class GoodChild(Base):
    __slots__ = ['y']   # no __dict__

class BadChild(Base):
    pass                 # __dict__ is back

To maintain slot-only storage across an inheritance hierarchy, every class in the chain needs to declare __slots__ — even if that means an empty list for leaf classes that add no new attributes.

Multiple inheritance with slots introduces the “layout conflict” problem. CPython stores slots from multiple base classes contiguously, but if two parents define different slot layouts, Python cannot safely merge them. The result is TypeError: multiple bases have instance lay-out conflict. The practical rule: only one class in a multiple-inheritance graph should carry actual slot storage.

Adding Weak Reference Support

Slots remove several automatically created object-level attributes by default, including support for weakref.ref(). If you need weak references to instances, you must explicitly include __weakref__ in the slots list:

import weakref

class Node:
    __slots__ = ['value', '__weakref__']

    def __init__(self, value):
        self.value = value

n = Node(42)
r = weakref.ref(n)
print(r())   # <__main__.Node object at 0x...>

Without __weakref__ in __slots__, attempting to create a weakref.ref() on the instance raises TypeError.

Default Values

__slots__ itself has no mechanism for default values — it only declares which names are allowed. Handle defaults the normal way, through __init__ parameters:

class Point:
    __slots__ = ['x', 'y']

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

p = Point()       # (0, 0)
q = Point(3, 4)   # (3, 4)

You can also use __defaults__ on __init__ as usual:

class Point:
    __slots__ = ['x', 'y']

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y

print(Point.__init__.__defaults__)   # (0, 0)

slots With Properties

Slots only restrict data attributes. You can still define methods and properties normally — computed attributes via @property are method descriptors and are not stored in instance memory at all.

You can include properties in the slots list and assign them as slot values using property() directly:

class Circle:
    __slots__ = ['_radius', 'area']

    def __init__(self, radius):
        self._radius = radius
        self.area = radius ** 2 * 3.14159

    radius = property(lambda self: self._radius)

c = Circle(5)
print(c.radius)   # 5
print(c.area)     # 78.53975

A more common pattern is keeping properties as class-level methods (not in __slots__) and only listing the underlying data attributes in the slots declaration.

@dataclass(slots=True)

Python 3.10 added slots=True to the @dataclass decorator, which automatically generates __slots__ for every field:

from dataclasses import dataclass

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

This generates exactly the same result as writing the slots manually. The dataclass machinery handles field ordering, defaults, and __init__ generation while slots handle the memory optimization. Weak reference support still requires a separate field declaration:

from dataclasses import dataclass, field

@dataclass(slots=True)
class Node:
    value: int
    _refs: list = field(default_factory=list, repr=False)
    __weakref__: object = field(default=None, init=False)

Privacy and slots

Slots do not make attributes private. The _ClassName__attr name mangling that Python applies to double-underscore attributes works exactly the same way with slots. Use single-underscore prefixes to signal intended privacy — that’s the same convention as non-slotted classes.

When to Use slots

The memory savings compound at scale. A class with 3-5 attributes instantiated tens of thousands of times is a good candidate. Frames in a game engine, nodes in a graph data structure, protocol message objects — these all benefit.

For one-off classes or classes that rarely get instantiated, the added verbosity of __slots__ is usually not worth it. The complexity of inheritance chains with slots also demands more care.

The primary use case is high-volume instantiation where RAM is the bottleneck, and dataclasses with slots=True are the modern, cleanest way to get that benefit.

See Also

  • __str__ — define the human-readable string representation of an object
  • __new__ — control instance creation before __init__ runs
  • __init__ — initialize instance state after creation