__slots__ & Memory Optimization
__slots__ is a class declaration that lists exactly which attributes instances may have, letting Python drop the per-instance __dict__ and store those attributes in fixed slots — making each object smaller and attribute access a touch faster.
Learn __slots__ & Memory Optimization in our free Python course — an interactive lesson with runnable examples, a practice exercise and a quick reference.
Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
When you create millions of small objects, the hidden __dict__ on every instance dominates your memory use. __slots__ trades a little flexibility for substantial savings — the right tool when you have many instances and few attributes.
By default, Python stores an object's attributes in a dictionary attached to each instance — __dict__ . It's flexible (you can add any attribute, anytime) but every instance pays the cost of that dictionary:
That dictionary is convenient, but for one or two attributes it's a lot of overhead — and it repeats for every single instance you create.
Add a class-level __slots__ listing your attribute names and Python reserves fixed storage for exactly those — and removes __dict__ entirely:
The class behaves the same from the outside — you still write p.x — but each instance is now leaner because there's no dictionary riding along. The attributes live in compact, fixed slots instead.
Here's the trade-off. Without a __dict__ , you can't tack on attributes that aren't in __slots__ . Trying to do so raises AttributeError :
sys.getsizeof reports an object's size in bytes. A regular instance is small on its own, but it drags along a separate __dict__ — so the true cost is the instance plus its dictionary. A slotted instance has no such dictionary:
One object: irrelevant. A million objects: the __dict__ overhead can be hundreds of megabytes. Slots shine for data-heavy structures — graph nodes, simulation particles, parsed records:
Python 3.10+ generates the slots for you when you ask a dataclass for them:
Complete the class so it stores only lat and lon with no __dict__ . Replace each ___ , then run it.
❌ A single string instead of a tuple of names
✅ Always use a tuple (or list) of strings, even for one slot:
✅ Add it to __slots__ , or if you truly need dynamic attributes, include "__dict__" in the slots (which gives back the dictionary and most of the savings are lost).
❌ Expecting cached_property to work with slots
✅ Slots remove __dict__ , which cached_property relies on. Use a plain @property , or don't slot that class.
Build a slotted Vec2 with x and y and a length method, prove it has no __dict__ , and confirm an undeclared attribute is rejected.
Lesson complete — you can optimize memory now!
You understand the per-instance __dict__ , how __slots__ drops it for smaller, faster objects, the no-new-attributes restriction, how to measure with sys.getsizeof , and when slots are worth it. Use them deliberately, at scale.
🚀 Up next: Scope — global, nonlocal & LEGB — master where your names live.
Practice quiz
What does declaring __slots__ on a class do to its instances?
- It removes the per-instance __dict__ and stores attributes in fixed slots
- It makes every attribute read-only
- It automatically adds a __dict__ to each instance
- It speeds up class creation but not attribute access
Answer: It removes the per-instance __dict__ and stores attributes in fixed slots. __slots__ reserves fixed storage for the listed attributes and drops the per-instance __dict__, making instances smaller.
After class Point: __slots__ = ('x','y'), what is hasattr(Point(1,2), '__dict__')?
- True
- False
- It raises AttributeError
- None
Answer: False. A fully slotted class has no per-instance __dict__, so hasattr returns False.
What happens when you assign an attribute that is NOT listed in __slots__?
- It is silently added to a hidden __dict__
- It works but uses extra memory
- It raises AttributeError
- It raises TypeError
Answer: It raises AttributeError. Without a __dict__, assigning an undeclared attribute raises AttributeError — the restriction is the whole point.
Why is __slots__ described as a memory optimization?
- It compresses attribute values
- It eliminates the per-instance dictionary, which matters across many instances
- It shares attributes between all instances
- It moves attributes to disk
Answer: It eliminates the per-instance dictionary, which matters across many instances. Dropping the __dict__ on every instance saves memory that multiplies across millions of small objects.
Which is the correct way to declare a single slot?
- __slots__ = 'x'
- __slots__ = ('x',)
- x
Answer: __slots__ = ('x',). Use a tuple (note the trailing comma). A bare string is iterated character by character, which breaks for multi-letter names.
Since which Python version can a dataclass generate slots with @dataclass(slots=True)?
- 3.6
- 3.8
- 3.10
- 2.7
Answer: 3.10. @dataclass(slots=True) was added in Python 3.10, generating a slotted class automatically.
Which function reports an object's size in bytes for measuring savings?
- len()
- sys.getsizeof()
- id()
- sys.getrefcount()
Answer: sys.getsizeof(). sys.getsizeof reports an object's size in bytes; a slotted instance has no separate __dict__ to add to the total.
Why does @cached_property typically fail on a slotted class?
- cached_property is deprecated
- It needs a writable __dict__ to store the cache, which slots remove
- It only works on functions, not methods
- Slots make all attributes immutable
Answer: It needs a writable __dict__ to store the cache, which slots remove. cached_property stores its cached value in the instance __dict__, which a fully slotted class does not have.
When is __slots__ MOST worth using?
- When you have a handful of objects with many attributes
- When you create a very large number of small instances with a fixed set of attributes
- Whenever you define any class, as a habit
- Only for classes that need dynamic attributes
Answer: When you create a very large number of small instances with a fixed set of attributes. Slots shine at scale: many instances and few, fixed attributes. For a few objects the savings are irrelevant.
How can you keep a __dict__ AND use __slots__ if you truly need dynamic attributes?
- Include '__dict__' in the __slots__ tuple
- It is impossible to combine them
- Set __slots__ = None
- Use @property on every attribute
Answer: Include '__dict__' in the __slots__ tuple. Adding '__dict__' to __slots__ gives back the dictionary, but most of the memory savings are then lost.