Fixed-Offset Memory Layout Optimization (__slots__)
Master CPython instance memory optimization: suppressing __dict__ via __slots__, PyMemberDescrObject struct offsets, memory benchmarking, and inheritance layout constraints.
By default, every Python instance allocates an internal dictionary __dict__ to store dynamic attributes. While this enables adding new attributes on the fly, it imposes massive memory overhead for high-volume objects: a single standard object can easily consume over 150 bytes of RAM.
When building applications that instantiate millions of records (such as financial ticks, game entities, or scientific nodes), specifying __slots__ optimizes memory layouts by replacing the per-instance dictionary with a compact C struct array of fixed-offset pointer slots.
Eliminating instance __dict__ reduces memory per instance from ~152 bytes to ~48 bytes.
1. Comparing Standard vs Slotted Memory Layouts#
Mathematical Footprint Derivation#
Let represent attribute names defined in __slots__:
The memory compression ratio demonstrates substantial reduction:
2. C-API Implementation: Member Descriptors#
How does Python resolve attribute reads on slotted classes without a dictionary?
When __slots__ is declared:
type.__new__calculates instance memory sizetp_basicsizeas bytes.- For each attribute name in
__slots__, CPython creates a descriptor of typePyMemberDescrObjectand places it in the class dictionary. - Each member descriptor encapsulates a fixed byte offset from the instance base address.
- Accessing
obj.xexecutes the descriptor's__get__, which reads object memory directly at address , executing as a single-instruction pointer dereference bypassing dictionary hash lookups entirely!
123456789101112131415161718192021import sys class StandardPoint: def __init__(self, x: float, y: float): self.x = x self.y = y class SlottedPoint: __slots__ = ("x", "y") def __init__(self, x: float, y: float): self.x = x self.y = y std_p = StandardPoint(1.0, 2.0) slt_p = SlottedPoint(1.0, 2.0) print(f"Standard instance size : {sys.getsizeof(std_p)} bytes (excluding __dict__)") print(f"Standard __dict__ size : {sys.getsizeof(std_p.__dict__)} bytes") print(f"Total Standard Footprint: {sys.getsizeof(std_p) + sys.getsizeof(std_p.__dict__)} bytes") print(f"Slotted Total Footprint : {sys.getsizeof(slt_p)} bytes")
3. Critical Inheritance Traps with __slots__#
While __slots__ delivers exceptional space savings, it introduces rigid structural constraints:
__slots__ itself, CPython will automatically allocate an instance dictionary __dict__ for instances of the subclass, wiping out your memory savings!
To maintain the optimization, subclasses must explicitly define:
``python
class SubSlotted(SlottedPoint):
__slots__ = () # Keeps __dict__ suppressed!
__slots__. Because each parent attempts to place its slot attributes at conflicting byte offsets within the instance struct, CPython aborts with ValueError: multiple bases have instance lay-out conflict.4. Concept Verification#
Test your understanding of __slots__ memory optimization:
How does CPython read and write attributes on a class that defines __slots__ = ('x', 'y') without an instance dictionary?
What happens when a child class inherits from a slotted parent class but does not declare __slots__ itself?