Discover how Python’s __slots__ can significantly reduce memory usage and improve performance in backend systems. This guide walks you through the basics of slotted classes, explains how they work, and shows real-world backend examples.
When we build backend systems in Python, performance isn't always our first concern. But sometimes, especially when you have thousands or even millions of objects in memory like API response models, simulation agents, or data processing pipelines, memory usage and speed start to matter. One of the lesser-known Python features that can help in such cases is __slots__
.
In this blog, we'll explore __slots__
from beginner level to more advanced use cases, with clear examples and backend-oriented use cases.
__slots__
?In Python, every object by default stores its attributes in a special dictionary called __dict__
. This dictionary allows us to dynamically add, remove, or modify attributes at runtime.
class User:
def __init__(self, name, email):
self.name = name
self.email = email
u = User("Alice", "alice@example.com")
u.age = 30 # works just fine
But this flexibility comes at a cost. Every instance carries a dynamic dictionary, which adds memory overhead and makes attribute access a tiny bit slower. That's where __slots__
comes in. __slots__
lets you define a fixed set of attributes for a class. When used, Python doesn’t create a __dict__
for each instance, reducing memory usage.
class SlimUser:
__slots__ = ['name', 'email']
def __init__(self, name, email):
self.name = name
self.email = email
Now, trying to add a new attribute:
s = SlimUser("Bob", "bob@example.com")
s.age = 25 # AttributeError: 'SlimUser' object has no attribute 'age'
In backend systems, we often work with many lightweight objects:
In these cases, saving even a few bytes per object can scale into megabytes or more.
class Product:
def __init__(self, id, name, price):
self.id = id
self.name = name
self.price = price
Suppose you're loading 1 million Product
instances into memory for caching. Using __slots__
:
class SlimProduct:
__slots__ = ['id', 'name', 'price']
def __init__(self, id, name, price):
self.id = id
self.name = name
self.price = price
Using sys.getsizeof()
won't show the full memory benefit because it only shows shallow size, but if you compare .__dict__
and .__slots__
attributes or use tools like pympler
or tracemalloc
will show significant reduction when objects are deeply nested.
__slots__
Works Under the HoodWhen you use __slots__
, Python internally creates a more static structure like a C struct instead of a dynamic dictionary. It uses descriptors and a slot table to manage attribute access.
Benefits:
Drawbacks:
__slots__
and InheritanceSlotted classes can't be freely combined like regular ones. If you subclass a slotted class, you need to define __slots__
again even if it's empty.
class Base:
__slots__ = ['id']
class Child(Base):
__slots__ = ['name']
If you forget to redefine __slots__
, Python will revert to using __dict__
in the subclass.
You can also include __dict__
in the slots explicitly if you want partial flexibility:
class PartiallyDynamic:
__slots__ = ['id', '__dict__']
@dataclass
with slots=True
Python 3.10+ allows using __slots__
automatically with dataclasses
:
from dataclasses import dataclass
@dataclass(slots=True)
class Product:
id: int
name: str
price: float
This is much cleaner and combines the benefits of dataclasses like auto-generated __init__
, __repr__
, etc. with memory savings.
Suppose you're building a high-throughput API that returns lots of small JSON records from Redis or a database. Each record is parsed into a Python object.
class CachedItem:
__slots__ = ['id', 'category', 'value']
def __init__(self, id, category, value):
self.id = id
self.category = category
self.value = value
This keeps your memory usage lean, especially if you're storing thousands of these in memory. You can even combine __slots__
with __slots__ + property
to make fields read-only:
class ImmutableItem:
__slots__ = ['_id']
def __init__(self, id):
self._id = id
@property
def id(self):
return self._id
__slots__
Don’t use __slots__
if:
__dict__
introspectionIt’s a tool, not a silver bullet. Use it when profiling shows memory usage is actually a concern.
__slots__
is a powerful but often overlooked feature in Python that can help reduce memory usage and improve attribute access performance in certain use cases. Especially in backend systems where you handle lots of structured but repetitive objects like response models or cached entities, slotted classes can provide measurable performance benefits.
Use them with care, test them in your architecture, and always validate with profiling tools before optimizing prematurely.