Python __slots__ Changes Instance Storage and Inheritance
How __slots__ creates member descriptors, removes automatic instance dictionaries, and affects subclasses and weak references.
Declaring __slots__ asks Python to create a fixed set of member descriptors for a class. Unless a base class already provides them or the declaration includes their names, instances do not receive an automatic __dict__ or __weakref__ slot.
That can reduce per-instance storage and may speed attribute access, but it also changes the class’s extension and inheritance behavior. It is not merely a memory switch. The details here follow the Python 3.14.7 data-model specification for __slots__.
Slot names become descriptors
class Coordinate:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
point = Coordinate(3, 4)
assert not hasattr(point, "__dict__")
try:
point.label = "start"
except AttributeError:
pass
else:
raise AssertionError("undeclared attribute was accepted")
Coordinate.__dict__["x"] is a descriptor that stores and retrieves the corresponding per-instance slot. Because the descriptor occupies the class attribute name, assigning Coordinate.x = 0 later overwrites it and breaks normal access to that slot. Set defaults in __init__, not by replacing a slot descriptor with a class value.
Slot declarations do not enforce types, validation, or immutability. point.x can still be rebound or deleted. A property or custom descriptor is appropriate when assignment needs policy.
A subclass must make its own storage choice
class ColoredCoordinate(Coordinate):
__slots__ = ("color",)
def __init__(self, x, y, color):
super().__init__(x, y)
self.color = color
colored = ColoredCoordinate(3, 4, "blue")
assert not hasattr(colored, "__dict__")
The subclass inherits the base slots and adds one. If ColoredCoordinate omitted __slots__, its instances would gain __dict__ and __weakref__, restoring arbitrary instance attributes. Use __slots__ = () in a subclass that adds no fields but should retain the base’s storage policy.
The opposite direction cannot remove a dictionary. If any base lacks __slots__, instances of its slotted descendants still have that inherited __dict__ and __weakref__ storage.
Do not repeat a base slot name in a subclass. Python 3.14.7 documents the resulting program as undefined because the new descriptor hides ordinary access to the base slot. Code that needs inherited slot names can inspect Base.__slots__; dataclasses.fields() is the right API for dataclass fields.
Weak references require an explicit slot
import weakref
class Node:
__slots__ = ("value", "__weakref__")
def __init__(self, value):
self.value = value
node = Node("root")
reference = weakref.ref(node)
assert reference() is node
Without "__weakref__", a purely slotted instance cannot be the target of weakref.ref. Add it once in the inheritance tree when weak-reference support is part of the API. Similarly, include "__dict__" when consumers must be able to attach dynamic attributes. Either choice gives back some storage that a minimal slotted layout removed.
Multiple inheritance restricts layouts
Python allows several slotted bases only when at most one contributes actual slot storage. Combining two unrelated bases with non-empty slot layouts raises TypeError during class creation:
class Left:
__slots__ = ("left",)
class Right:
__slots__ = ("right",)
try:
class Combined(Left, Right):
__slots__ = ()
except TypeError:
pass
else:
raise AssertionError("conflicting layouts were accepted")
Empty-slot mixins work well because they add behavior without another layout. If independent bases both need stored state, composition is usually simpler than forcing their layouts into one instance.
Measure the benefit on the real class
The documentation says storage savings and lookup improvements can be significant, but the exact result depends on the Python implementation, version, field count, inheritance tree, and how memory is measured. sys.getsizeof(instance) alone can mislead because a normal instance’s separate dictionary is another object.
Use __slots__ when a class will have many instances and a fixed field set, then benchmark the deployed interpreter. Avoid it when dynamic attributes, weak references, multiple inheritance, serialization tools, or frameworks expect an instance dictionary unless you have tested the integration and declared the needed slots.