Code for Scale: What Every Python Developer Should Know About '__slots__'
Imagine this:
You're developing a web scraper to gather product data from thousands of e-commerce pages. For each product, you store a name, price, and availability status. You create a Product class, and before you know it, you're dealing with millions of instances across different categories and sites.
Everything works fine at first — until your script starts slowing down and memory usage shoots through the roof.
But hey — each product only stores three simple values. Why is Python being so wasteful?
Turns out, the default Python object model keeps a memory-hungry secret. Unless you know about __slots__, your minimalist class is secretly using more memory than it should.
In this post, let's explore how Python stores object attributes, how __slots__ can drastically reduce memory overhead, and when (and when not) to use it.
Python Classes Are Flexible — And Heavy
By default, Python class instances are extremely flexible. You can dynamically add attributes to them at runtime, delete them, even monkey-patch methods. This flexibility is made possible because Python stores attributes in a dictionary: __dict__.
class Regular:
def __init__(self, name, price, available):
self.name = name
self.price = price
self.available = available
obj = Regular("Laptop", 999.99, True)
print(obj.__dict__) # {'name': 'Laptop', 'price': 999.99, 'available': True}
Each instance carries its own __dict__, which adds memory overhead. If you're only creating a handful of instances, that overhead doesn't matter. But scale that to millions of objects, and you're in for a surprise.
How __slots__ Slims Down Your Classes
__slots__ is a special attribute that tells Python not to use a dynamic dictionary for attribute storage. Instead, it creates a more memory-efficient structure internally, closer to how objects work in statically typed languages like C.
class Slotted:
__slots__ = ('name', 'price', 'available')
def __init__(self, name, price, available):
self.name = name
self.price = price
self.available = available
Here, Slotted instances won't have a __dict__, and Python stores attributes in a predefined internal structure, reducing per-object memory footprint.
Real-World Memory Comparison
Let's pit our Regular class against the Slotted one with a little help from the pympler library.
Step 1: Install pympler
pip install pympler
Step 2: Run the benchmark
from pympler import asizeof
class Regular:
def __init__(self, name, price, available):
self.name = name
self.price = price
self.available = available
class Slotted:
__slots__ = ('name', 'price', 'available')
def __init__(self, name, price, available):
self.name = name
self.price = price
self.available = available
# Create a million objects
regular_objs = [Regular("Laptop", 999.99, True) for _ in range(10**6)]
slotted_objs = [Slotted("Laptop", 999.99, True) for _ in range(10**6)]
size_of_regular_objs = asizeof.asizeof(regular_objs)
size_of_slotted_objs = asizeof.asizeof(slotted_objs)
percentage_memory_saved = ((size_of_regular_objs - size_of_slotted_objs) / size_of_regular_objs) * 100
print("Total memory for Regular objects:", size_of_regular_objs, "bytes")
print("Total memory for Slotted objects:", size_of_slotted_objs, "bytes")
print(f"Percentage of memory saved: {percentage_memory_saved:.2f}%")
Output:
Total memory for Regular objects: 152448984 bytes
Total memory for Slotted objects: 64448832 bytes
Percentage of memory saved: 57.72%
That's ~58% less memory usage — a massive win for large-scale systems.
When Does __slots__ Deserve a Spot in Your Code?
__slots__ isn't a silver bullet. For many Python classes it's overkill. If your class needs to stay dynamic, support monkey-patching, or work well with certain libraries and frameworks, sticking with the default is often the better choice.
That said, __slots__ can be a game-changer in the right scenarios.
Consider using __slots__ when:
- You're defining simple data containers
- You're creating a large number of objects
- You don't need dynamic attributes
Common use cases:
- Product or data model classes in scrapers
- Geometry points, vectors, game entities
- Simulation entities (e.g. particles, agents)
Where __slots__ Falls Short
- You cannot add new attributes at runtime that aren't listed in
__slots__. This restricts the flexibility Python is known for. - Without
__dict__, introspection, debugging, and certain serialization techniques can become more complex. - Multiple inheritance becomes tricky, especially if only some parent classes define
__slots__.
Slotting It All Together
Python's __slots__ is a low-effort, high-reward optimization technique. If you're building performance-sensitive applications or simply want to write cleaner and leaner code, it's worth adding this tool to your arsenal.
In a world obsessed with scaling, even the tiniest savings per object can have a huge impact. Try it out — your RAM will thank you.