Skip to Content
Course content

134: Slots for Memory-Efficient Classes

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Why should I care about slots if my classes already work?

In most of your daily coding, you won't notice a thing. But here is the deal: by default, Python stores instance attributes in a dictionary called __dict__. Dictionaries are great because they're flexible—you can slap a new attribute onto an object whenever you feel like it. The problem is that dictionaries have a significant memory overhead.

Imagine you're building a system to track millions of GPS coordinates for a fleet of delivery drones. If each Coordinate object has its own __dict__, you're wasting a massive amount of RAM just on the dictionary structure itself, not the actual data. That's where __slots__ comes in. It tells Python, "I know exactly which attributes this class will have; don't bother creating a dictionary."

import sys

class Coordinate:
    __slots__ = ('lat', 'lon') # This is the magic line
    def __init__(self, lat, lon):
        self.lat = lat
        self.lon = lon

# Now Python allocates a small, fixed amount of space for each instance
# instead of a dynamic hash map.

How much of a difference does this actually make in memory?

It's often surprising. I've seen cases where switching to slots reduced memory usage by 40% to 60%. Because Python no longer needs to store the keys for every single instance, the memory footprint drops significantly. Plus, you get a slight bump in attribute access speed because Python can use a more direct offset to find the value in memory rather than performing a hash map lookup.

I usually only reach for this when I know I'm instantiating thousands—or millions—of the same object. If you're just making a few configuration objects for your app, stick to the defaults. The flexibility of __dict__ is usually worth more than a few kilobytes of saved RAM.

Does this mean I can't add new attributes on the fly anymore?

Exactly. That is the price you pay for the efficiency. When you define __slots__, you are locking down the object. If you try to assign an attribute that isn't listed in your slots, Python will throw an AttributeError.

coord = Coordinate(40.7128, -74.0060)
coord.lat = 40.7129 # This works fine

# But wait, I want to add a label to this specific coordinate...
try:
    coord.label = "NYC Office"
except AttributeError as e:
    print(f"Error: {e}") # Output: 'Coordinate' object has no attribute 'label'

I've seen developers try to "cheat" by adding '__dict__' to the slots list. While that works and gives you the best of both worlds (fixed slots for some, a dict for others), it completely defeats the purpose of memory optimization. If you find yourself needing dynamic attributes, you probably shouldn't be using slots in the first place.

What happens if I use slots in a class hierarchy?

This is where things get a bit trippy. If you have a base class with __slots__ and a subclass that doesn't define them, the subclass will still have a __dict__. This means your memory savings vanish the moment you inherit from a slotted class without also slotting the child.

To keep the memory efficiency flowing down the chain, every single class in the inheritance line needs to define __slots__. Even if the subclass doesn't add any new attributes, you should define __slots__ = () to ensure it doesn't accidentally create a __dict__.




📋 Practical Task

Optimizing a High-Frequency Sensor Log

You are writing a script to process a massive log file containing millions of sensor readings. Each reading has a sensor_id (integer), a value (float), and a timestamp (float). Your current implementation is crashing the server due to an Out-Of-Memory (OOM) error.

Your task:

  • Create a class named SensorReading that uses __slots__ to minimize memory usage.
  • Ensure it supports sensor_id, value, and timestamp.
  • Initialize these values in the __init__ method.
  • Write a small test script that instantiates one SensorReading and attempts to assign a new attribute self.unit = "Celsius" to it. Wrap this in a try/except block to verify that the AttributeError is raised, proving your slots are working.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.