Python __slots__: Weniger RAM, gleicher Code
Sonntag, 22. März 2026
Stell dir vor, du erzeugst in deinem Programm eine Million Objekte. Klingt viel, aber bei Datenverarbeitungs-Pipelines, Spielen oder Event-Systemen ist das schneller erreicht als man denkt. Und plötzlich frisst dein Prozess 800 MB RAM, obwohl die Daten selbst eigentlich in 200 MB passen sollten.
Einer der Gründe dafür ist Pythons __dict__. Und __slots__ ist die elegante Lösung dagegen – ohne dass du deinen Code groß umschreiben musst.
Was steckt hinter dem Problem?
Jedes Python-Objekt trägt standardmäßig ein __dict__ mit sich. Das ist ein Dictionary, das alle Instanzattribute speichert. Praktisch, weil du jederzeit dynamisch neue Attribute hinzufügen kannst:
class User:
def __init__(self, name: str):
self.name = name
u = User("Anna")
u.nickname = "Anni" # Kein Problem, geht einfach
u.whatever = 42 # Auch das geht
Diese Flexibilität hat ihren Preis. Ein leeres Python-Dictionary kostet schon mal rund 200 Bytes. Mal einer Million Objekte – du siehst das Problem.
Außerdem ist der Lookup über __dict__ etwas langsamer als ein direkter Slot-Zugriff, weil Python erst das Dictionary durchsucht.
slots einführen
Mit __slots__ sagst du Python: Diese Klasse hat genau diese Attribute, und keine anderen. Python ersetzt das __dict__ durch feste C-Level-Slots – deutlich kompakter und schneller.
class User:
__slots__ = ("name", "age", "email")
def __init__(self, name: str, age: int, email: str):
self.name = name
self.age = age
self.email = email
Das war’s. Der Rest des Codes bleibt identisch. Du greifst weiterhin mit user.name auf das Attribut zu, kannst es lesen und setzen – alles wie vorher.
Was sich ändert: Kein __dict__ mehr, kein dynamisches Hinzufügen von Attributen, und deutlich weniger RAM pro Instanz.
Wie viel macht das wirklich aus?
Schauen wir uns konkrete Zahlen an. Mit sys.getsizeof() kann man den Speicherverbrauch messen:
import sys
class UserDict:
def __init__(self, name, age):
self.name = name
self.age = age
class UserSlots:
__slots__ = ("name", "age")
def __init__(self, name, age):
self.name = name
self.age = age
u_dict = UserDict("Anna", 30)
u_slots = UserSlots("Anna", 30)
print(sys.getsizeof(u_dict)) # ~48 Bytes (Objekt) + ~232 Bytes (__dict__)
print(sys.getsizeof(u_slots)) # ~56 Bytes (Objekt, kein __dict__)
Klingt nach wenig. Aber der eigentliche Unterschied wird sichtbar, wenn du viele Instanzen erzeugst. In der Praxis spart __slots__ oft 40–60 % RAM – je nach Anzahl und Größe der Attribute.
Hier ein direkter Vergleich mit einer Million Objekten:
import tracemalloc
tracemalloc.start()
users = [UserDict("Anna", 30) for _ in range(1_000_000)]
current, peak = tracemalloc.get_traced_memory()
print(f"Ohne __slots__: {peak / 1024 / 1024:.1f} MB")
tracemalloc.stop()
tracemalloc.start()
users = [UserSlots("Anna", 30) for _ in range(1_000_000)]
current, peak = tracemalloc.get_traced_memory()
print(f"Mit __slots__: {peak / 1024 / 1024:.1f} MB")
tracemalloc.stop()
Typische Ausgabe: ohne Slots ~280 MB, mit Slots ~115 MB. Mehr als die Hälfte gespart, ohne eine einzige Zeile Logik zu ändern.
Was du verlierst
Nichts ist umsonst. Mit __slots__ kannst du keine neuen Attribute zur Laufzeit hinzufügen:
u = UserSlots("Anna", 30)
u.nickname = "Anni" # AttributeError: 'UserSlots' object has no attribute 'nickname'
Das ist meistens kein Problem – wenn du weißt, welche Attribute eine Klasse hat, brauchst du die Flexibilität nicht. Aber es ist gut, das im Hinterkopf zu behalten.
Außerdem gibt es eine Feinheit bei der Vererbung: Wenn eine Subklasse kein eigenes __slots__ definiert, bekommt sie wieder ein __dict__ zurück – und der Vorteil ist weg.
class Admin(UserSlots):
pass # Kein __slots__ → __dict__ ist wieder da!
class Admin(UserSlots):
__slots__ = ("permissions",) # Richtig: eigene Slots definieren
Vererbungshierarchien mit __slots__ erfordern also etwas Aufmerksamkeit.
Wann lohnt sich slots?
Ehrlich gesagt: Nicht immer. Für eine Klasse, von der du zwei oder drei Instanzen erzeugst, macht es keinen messbaren Unterschied. Der Aufwand lohnt sich, wenn:
- Du viele Instanzen erzeugst (Tausende bis Millionen)
- Die Klasse wenige, feste Attribute hat
- RAM-Verbrauch ein echtes Problem ist
- Du Speed-kritische Pfade hast, bei denen Attribut-Lookups zählen
Datenklassen für Parsing-Pipelines, Knoten in Baumstrukturen, Entities in Spielen – das sind gute Kandidaten.
slots und dataclasses
Eine Frage, die öfter auftaucht: Funktioniert das mit @dataclass?
Ja! Ab Python 3.10 gibt es einen direkten Parameter:
from dataclasses import dataclass
@dataclass(slots=True)
class User:
name: str
age: int
email: str
Das ist die sauberste Kombination: Du bekommst den Komfort von Dataclasses und den Speichervorteil von __slots__, ohne beides manuell zusammenklöppeln zu müssen.
Für Python 3.7–3.9 kannst du __slots__ manuell zur Dataclass hinzufügen, aber dann musst du aufpassen, dass du keine Felder mit Defaults verwendest – das führt zu einem Konflikt. Ab 3.10 löst der slots=True-Parameter das automatisch.
Kurz zusammengefasst
__slots__ ist ein einfaches Mittel, um den RAM-Verbrauch von Python-Klassen deutlich zu senken:
- Ersetzt
__dict__durch feste, kompakte C-Level-Slots - Spart in der Praxis 40–60 % Speicher pro Instanz
- Unterbindet dynamisches Hinzufügen von Attributen
- Subklassen brauchen eigene
__slots__, sonst kehrt__dict__zurück - Ab Python 3.10:
@dataclass(slots=True)als schlanke Kombination
Wenn du das nächste Mal merkst, dass dein Prozess RAM frisst wie ein hungriger Teenager – check zuerst, ob __slots__ helfen kann.