🧠 Python DeepCuts — 💡 The Power of __slots__ Revisited
Posted on: December 24, 2025
Description:
slots is often introduced as a memory optimization trick. But beneath the surface, it fundamentally changes Python’s object memory layout and attribute access model.
This DeepCut revisits slots from an internal perspective:
- how attributes are stored by default
- what actually disappears when slots are enabled
- why attribute lookup becomes more efficient
- when slots are helpful — and when they hurt
🧩 Default Attribute Storage: dict
By default, every Python object stores its attributes in a per-instance dictionary.
class Normal:
def __init__(self, x, y):
self.x = x
self.y = y
n = Normal(10, 20)
n.__dict__
Each instance carries:
- a pointer to a dictionary
- a hash table for keys and values
- dynamic growth capability
This is flexible — but memory-heavy.
🧠 What slots Changes Internally?
When slots is defined:
- Python removes the per-instance dict
- Attribute names are fixed at class creation
- Storage becomes a compact, array-like structure
class Slotted:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
Instances no longer carry a dictionary — attributes are stored by offset, not by name lookup.
🔍 Memory Footprint: Dict vs Slots:
The real memory savings come from eliminating dict.
import sys
normal = Normal(1, 2)
slotted = Slotted(1, 2)
sys.getsizeof(normal)
sys.getsizeof(slotted)
While the object size difference may appear small, the missing dictionary scales massively when creating thousands or millions of objects.
This is why slots matter in:
- ORMs
- simulations
- AST nodes
- compiler internals
- ML feature objects
🧱 Attribute Lookup Paths:
Attribute access differs internally:
- Normal objects → obj.dict['x']
- Slotted objects → fixed memory offset
normal.__dict__["x"]
slotted.x
Slots reduce:
- hash lookups
- pointer indirections
- cache misses
Result: slightly faster attribute access with much lower memory overhead.
⚠️ Slots Restrict Dynamic Attributes:
Slots intentionally prevent adding new attributes at runtime.
try:
slotted.z = 30
except AttributeError as e:
print(e)
This is a feature — not a bug — enforcing a fixed object schema.
However, it makes slots unsuitable for:
- monkey-patching
- dynamic data models
- evolving schemas
🧬 Inheritance and slots:
Slots don’t automatically merge across inheritance hierarchies.
class Base:
__slots__ = ("a",)
class Child(Base):
__slots__ = ("b",)
c = Child()
c.a = 1
c.b = 2
Each class defines its own slot layout.
Mismanaging this can lead to subtle bugs or missing attributes.
🔄 Hybrid Slots: Allowing dict Explicitly:
You can opt back into dynamic attributes by including dict.
class Hybrid:
__slots__ = ("x", "__dict__")
h = Hybrid()
h.x = 10
h.y = 20
🧠 When slots Is Worth Using?
Use slots when:
- You create many instances
- Attribute names are fixed
- Memory pressure matters
- Performance predictability is important
Avoid slots when:
- You rely on dynamic attributes
- You expect frequent schema changes
- You need heavy runtime introspection
✅ Key Points:
- Default objects store attributes in dict
- slots removes the per-instance dictionary
- Attributes become fixed offsets in memory
- Lookup becomes faster and more cache-friendly
- Slots enforce structure and reduce memory usage
- Inheritance and flexibility must be handled carefully
slots is not a micro-optimization — it’s a structural design choice.
Code Snippet:
import sys
class Normal:
def __init__(self, x, y):
self.x = x
self.y = y
class Slotted:
__slots__ = ("x", "y")
def __init__(self, x, y):
self.x = x
self.y = y
n = Normal(10, 20)
s = Slotted(10, 20)
print("Normal __dict__:", n.__dict__)
print("Slotted has __dict__:", hasattr(s, "__dict__"))
print("Normal size:", sys.getsizeof(n))
print("Slotted size:", sys.getsizeof(s))
print("Normal x via dict:", n.__dict__["x"])
print("Slotted x direct:", s.x)
try:
s.z = 30
except AttributeError as e:
print("Slot restriction:", e)
class Base:
__slots__ = ("a",)
class Child(Base):
__slots__ = ("b",)
c = Child()
c.a = 1
c.b = 2
print("Inherited slots:", c.a, c.b)
class Hybrid:
__slots__ = ("x", "__dict__")
h = Hybrid()
h.x = 10
h.y = 20
print("Hybrid attrs:", h.__dict__)
No comments yet. Be the first to comment!