CodeNebula
Python🚀Advancedadvanced

`__slots__` — Memory Optimization

Python · Advanced · Run with python3

Explanation

By default, Python stores instance attributes in a `__dict__` per instance — flexible but memory-heavy (each dict has overhead). Declaring `__slots__ = ("x", "y")` on a class replaces the instance dict with a fixed-size array, reducing memory by 40-70% and slightly speeding attribute access. Trade-offs: no arbitrary attributes, no `__dict__` (unless you include it), and subclasses must also declare slots for the full benefit. Python 3.10's `@dataclass(slots=True)` generates slots automatically.

Real-World Scenario

A spatial index stores millions of `Point(x, y, z)` objects. Without `__slots__`, each instance has a `__dict__` taking ~232 bytes. With `__slots__`, the same object drops to ~56 bytes — a 4× memory reduction that keeps the entire index in L3 cache. ```python import sys class PointDict: def __init__(self, x, y, z): self.x, self.y, self.z = x, y, z class PointSlots: __slots__ = ("x", "y", "z") def __init__(self, x, y, z): self.x, self.y, self.z = x, y, z pd = PointDict(1.0, 2.0, 3.0) ps = PointSlots(1.0, 2.0, 3.0) print(f"PointDict size: {sys.getsizeof(pd)} bytes + {sys.getsizeof(pd.__dict__)} dict") print(f"PointSlots size: {sys.getsizeof(ps)} bytes (no __dict__)") print(f"Has __dict__: PointDict={hasattr(pd, '__dict__')}, PointSlots={hasattr(ps, '__dict__')}") # Slots with inheritance (child must also declare slots) class Point4D(PointSlots): __slots__ = ("w",) def __init__(self, x, y, z, w): super().__init__(x, y, z) self.w = w ```

Quick Snippet

snippet.py
import sys

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

class PointSlots:
    __slots__ = ("x", "y", "z")
    def __init__(self, x, y, z):
        self.x, self.y, self.z = x, y, z

pd = PointDict(1.0, 2.0, 3.0)
ps = PointSlots(1.0, 2.0, 3.0)

print(f"PointDict  size: {sys.getsizeof(pd)} bytes + {sys.getsizeof(pd.__dict__)} dict")
print(f"PointSlots size: {sys.getsizeof(ps)} bytes (no __dict__)")
print(f"Has __dict__: PointDict={hasattr(pd, '__dict__')}, PointSlots={hasattr(ps, '__dict__')}")

# Slots with inheritance (child must also declare slots)
class Point4D(PointSlots):
    __slots__ = ("w",)
    def __init__(self, x, y, z, w):
        super().__init__(x, y, z)
        self.w = w

Full Example

slots.py
# Compile: python3 slots.py
# Output:
# PointDict and PointSlots sizes printed

import sys

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

class PointSlots:
    __slots__ = ("x", "y", "z")
    def __init__(self, x, y, z):
        self.x, self.y, self.z = x, y, z

pd = PointDict(1.0, 2.0, 3.0)
ps = PointSlots(1.0, 2.0, 3.0)

dict_overhead = sys.getsizeof(pd.__dict__) if hasattr(pd, "__dict__") else 0
print(f"PointDict  size: {sys.getsizeof(pd)} + {dict_overhead} dict = {sys.getsizeof(pd) + dict_overhead} bytes")
print(f"PointSlots size: {sys.getsizeof(ps)} bytes (no __dict__)")
print(f"Memory saved: ~{sys.getsizeof(pd) + dict_overhead - sys.getsizeof(ps)} bytes per instance")

# Verify no __dict__ on slotted class
print(f"PointDict  has __dict__: {hasattr(pd, '__dict__')}")
print(f"PointSlots has __dict__: {hasattr(ps, '__dict__')}")

# Slots with inheritance
class Point4D(PointSlots):
    __slots__ = ("w",)
    def __init__(self, x, y, z, w):
        super().__init__(x, y, z)
        self.w = w

p4 = Point4D(1, 2, 3, 4)
print(f"Point4D: ({p4.x}, {p4.y}, {p4.z}, {p4.w})")
Run command
python3 slots.py