- What is __slots__ β a special attribute that saves memory and speeds up classes
- Why use it β memory efficiency, performance, attribute control
- Basic usage β how to define __slots__ in your classes
- Memory savings β see how much memory you can save
- Inheritance β how __slots__ works with inheritance
- Limitations β what you can't do with __slots__
What is __slots__?
__slots__ is a special attribute in Python that lets you tell the interpreter exactly what attributes a class can have. It's like giving your class a fixed list of allowed attributes instead of letting it create them dynamically.
Think of __slots__ like a parking lot with assigned spaces. Without __slots__, you can park anywhere (any attribute). With __slots__, you have specific spots (attributes), and if you try to park somewhere else, you get turned away. This makes everything more organized and efficient.
By default, Python classes store attributes in a dictionary (__dict__). This is flexible but uses a lot of memory. __slots__ replaces this dictionary with a more efficient structure.
π‘ Key concept: __slots__ tells Python exactly what attributes to expect. This saves memory and makes attribute access faster.
Why Use __slots__?
The Benefits of __slots__
__slots__ gives you three main benefits:
# Why Use __slots__?
print("=" * 50)
print("WHY USE __slots__?")
print("=" * 50)
# ============================================================
# WITHOUT __slots__ β Flexible but Memory Heavy
# ============================================================
print("\nβ WITHOUT __slots__:")
class Person:
def __init__(self, name, age):
self.name = name
self.age = age
person = Person("Alice", 30)
# You can add ANY attribute at any time
person.city = "NYC" # Works!
person.email = "alice@example.com" # Works!
person.phone = "123-456-7890" # Works!
print(f" Person: {person.name}, {person.age}")
print(f" City: {person.city}")
print(f" Email: {person.email}")
print(f" Phone: {person.phone}")
# Each object has a __dict__ that stores all attributes
print(f" __dict__: {person.__dict__}")
print("\n β Problems:")
print(" β’ Accidental attributes can be created")
print(" β’ Each object has a dictionary (memory heavy)")
print(" β’ Attribute access is slightly slower")
# ============================================================
# WITH __slots__ β Controlled and Efficient
# ============================================================
print("\nβ
WITH __slots__:")
class PersonSlotted:
__slots__ = ['name', 'age'] # Only these attributes are allowed
def __init__(self, name, age):
self.name = name
self.age = age
person2 = PersonSlotted("Bob", 25)
print(f" Person: {person2.name}, {person2.age}")
# You can still access attributes normally
person2.name = "Robert"
print(f" Updated name: {person2.name}")
# But you CANNOT add new attributes
try:
person2.city = "LA"
except AttributeError as e:
print(f" β {e}")
# There is NO __dict__ (saves memory)
try:
print(f" __dict__: {person2.__dict__}")
except AttributeError as e:
print(f" β No __dict__: {e}")
print("\nβ
Benefits:")
print(" β’ Prevents accidental attribute creation")
print(" β’ Saves memory (no __dict__ per object)")
print(" β’ Faster attribute access")
# ============================================================
# BENEFITS SUMMARY
# ============================================================
print("\n" + "-" * 30)
print("BENEFITS OF __slots__")
print("-" * 30)
print("""
βββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ
β BENEFIT β WHAT IT MEANS FOR YOU β
βββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ€
β Memory Savings β No __dict__ per object, less memory used β
β β β
β Faster Access β Attribute access is faster β
β β β
β Prevents Errors β Can't accidentally create new attributes β
β β β
β Better Performance β 10-30% faster attribute access β
β β β
β Immutability β Restricts what attributes can exist β
βββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββ
π __slots__ is perfect for classes with many objects.
""")
Benefits of __slots__:
- Memory savings β no __dict__ per object
- Faster access β attribute access is quicker
- Prevents errors β can't accidentally create new attributes
- Better performance β 10-30% faster attribute access
- Immutability β restricts what attributes can exist
Quick Check: What does __slots__ do? (Answer: It tells Python exactly what attributes a class can have, saving memory and preventing accidental attribute creation)
Basic Usage
How to Use __slots__
Using __slots__ is very simple. Just define it as a class variable containing a list or tuple of attribute names.
# Basic __slots__ Usage
print("=" * 50)
print("BASIC __slots__ USAGE")
print("=" * 50)
# ============================================================
# DEFINING __slots__
# ============================================================
print("\n1. DEFINING __slots__")
class Student:
# List of allowed attributes
__slots__ = ['name', 'age', 'grade']
def __init__(self, name, age, grade):
self.name = name
self.age = age
self.grade = grade
def get_info(self):
return f"{self.name}, {self.age} years old, Grade: {self.grade}"
student = Student("Alice", 20, "A")
print(f" {student.get_info()}")
# All attributes work as normal
student.name = "Bob"
student.age = 21
print(f" Updated: {student.get_info()}")
# ============================================================
# __slots__ TUPLE VS LIST
# ============================================================
print("\n2. __slots__ TUPLE VS LIST")
# Both work, tuple is slightly more memory efficient
class UsingList:
__slots__ = ['x', 'y'] # List works
def __init__(self, x, y):
self.x = x
self.y = y
class UsingTuple:
__slots__ = ('x', 'y') # Tuple also works
def __init__(self, x, y):
self.x = x
self.y = y
print(" Both lists and tuples work for __slots__")
print(" β
Tuples are slightly more memory efficient")
# ============================================================
# __slots__ WITH METHODS
# ============================================================
print("\n3. __slots__ WITH METHODS")
class Calculator:
__slots__ = ['value']
def __init__(self, value):
self.value = value
def add(self, amount):
self.value += amount
return self.value
def multiply(self, amount):
self.value *= amount
return self.value
def get_value(self):
return self.value
calc = Calculator(5)
print(f" Value: {calc.get_value()}")
print(f" Add 3: {calc.add(3)}")
print(f" Multiply 2: {calc.multiply(2)}")
# ============================================================
# __slots__ AND DEFAULT VALUES
# ============================================================
print("\n4. __slots__ AND DEFAULT VALUES")
class Product:
__slots__ = ['name', 'price', 'stock']
def __init__(self, name, price, stock=0):
self.name = name
self.price = price
self.stock = stock # Default values work fine
product1 = Product("Laptop", 999.99, 10)
product2 = Product("Mouse", 29.99) # Uses default stock
print(f" Product 1: {product1.name}, ${product1.price}, Stock: {product1.stock}")
print(f" Product 2: {product2.name}, ${product2.price}, Stock: {product2.stock}")
# ============================================================
# __slots__ WITH @property
# ============================================================
print("\n5. __slots__ WITH @property")
class Temperature:
__slots__ = ['_celsius']
def __init__(self, celsius):
self._celsius = celsius
@property
def celsius(self):
return self._celsius
@celsius.setter
def celsius(self, value):
if value < -273.15:
raise ValueError("Temperature below absolute zero")
self._celsius = value
@property
def fahrenheit(self):
return self._celsius * 9/5 + 32
temp = Temperature(25)
print(f" Celsius: {temp.celsius}Β°C")
print(f" Fahrenheit: {temp.fahrenheit}Β°F")
temp.celsius = 30
print(f" After update - Celsius: {temp.celsius}Β°C")
# ============================================================
# KEY RULES
# ============================================================
print("\n" + "-" * 30)
print("KEY RULES FOR __slots__")
print("-" * 30)
print("""
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β RULES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β 1. Define __slots__ as a list or tuple of attribute names β
β 2. You can only assign attributes that are in __slots__ β
β 3. Classes with __slots__ don't have a __dict__ β
β 4. __slots__ works with @property and methods β
β 5. Default values work fine in __init__ β
β 6. Use tuple for slightly better memory efficiency β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
""")
Basic usage key points:
- __slots__ = ['name', 'age'] β defines allowed attributes
- List or tuple β both work, tuple is slightly better
- Methods work β you can have methods too
- Default values β work as expected in __init__
- @property β works perfectly with __slots__
Quick Check: How do you define __slots__ in a class? (Answer: Add __slots__ = ['attribute1', 'attribute2'] as a class variable)
Memory Savings
How Much Memory You Can Save
The main reason to use __slots__ is memory savings. Let's see how much difference it makes.
# Memory Savings with __slots__
print("=" * 50)
print("MEMORY SAVINGS WITH __slots__")
print("=" * 50)
import sys
# ============================================================
# CLASS WITHOUT __slots__
# ============================================================
print("\n1. CLASS WITHOUT __slots__")
class WithoutSlots:
def __init__(self, a, b, c, d):
self.a = a
self.b = b
self.c = c
self.d = d
# Create 1000 objects without __slots__
objects_without = [WithoutSlots(i, i*2, i*3, i*4) for i in range(1000)]
# Memory usage (approximate)
memory_without = sys.getsizeof(objects_without[0]) * 1000
print(f" Memory for 1000 objects: ~{memory_without:,} bytes")
print(f" Each object has __dict__: {objects_without[0].__dict__}")
# ============================================================
# CLASS WITH __slots__
# ============================================================
print("\n2. CLASS WITH __slots__")
class WithSlots:
__slots__ = ['a', 'b', 'c', 'd']
def __init__(self, a, b, c, d):
self.a = a
self.b = b
self.c = c
self.d = d
# Create 1000 objects with __slots__
objects_with = [WithSlots(i, i*2, i*3, i*4) for i in range(1000)]
# Memory usage (approximate)
memory_with = sys.getsizeof(objects_with[0]) * 1000
print(f" Memory for 1000 objects: ~{memory_with:,} bytes")
# No __dict__!
try:
print(f" __dict__: {objects_with[0].__dict__}")
except AttributeError:
print(" β
No __dict__ (memory saved)")
# ============================================================
# COMPARISON
# ============================================================
print("\n3. COMPARISON")
print(f" Without __slots__: ~{memory_without:,} bytes")
print(f" With __slots__: ~{memory_with:,} bytes")
print(f" Memory saved: ~{memory_without - memory_with:,} bytes")
print(f" Savings: ~{(1 - memory_with/memory_without) * 100:.1f}%")
print("\n β
With 1000 objects, __slots__ saves significant memory")
print(" β
For large-scale applications, this is huge")
# ============================================================
# REAL-WORLD EXAMPLE - 1 MILLION OBJECTS
# ============================================================
print("\n4. REAL-WORLD SCENARIO - 1 MILLION OBJECTS")
# Approximate memory usage for 1,000,000 objects
million_without = memory_without * 1000 # 1,000 objects * 1000 = 1,000,000
million_with = memory_with * 1000
print(f" Without __slots__: ~{million_without:,} bytes (~{million_without // 1024 // 1024} MB)")
print(f" With __slots__: ~{million_with:,} bytes (~{million_with // 1024 // 1024} MB)")
print(f" Memory saved: ~{(million_without - million_with) // 1024 // 1024} MB")
print("\n π‘ For large datasets, __slots__ is a game-changer!")
# ============================================================
# WHY MEMORY IS SAVED
# ============================================================
print("\n" + "-" * 30)
print("WHY MEMORY IS SAVED")
print("-" * 30)
print("""
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WITHOUT __slots__ β WITH __slots__ β
βββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββ€
β Each object has a β Each object has a fixed structure β
β dictionary (__dict__)β (no dictionary) β
β β β
β Dict is flexible β Structure is compact and efficient β
β but memory heavy β β
β β β
β ~50-60% more memory β Uses less memory β
β β β
β Good for flexibilityβ Good for performance & memory β
βββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββββ
π __slots__ replaces a dictionary with a fixed array.
""")
Memory savings key points:
- No __dict__ β saves about 50-60% memory
- Fixed structure β more compact than dictionary
- Scales well β even better with many objects
- Real impact β 1 million objects can save 40-60 MB
Quick Check: What's the main reason to use __slots__? (Answer: To save memory by eliminating the __dict__ from each object)
__slots__ and Inheritance
How __slots__ Works with Inheritance
__slots__ works differently with inheritance. The child class's __slots__ are combined with the parent's.
# __slots__ and Inheritance
print("=" * 50)
print("__slots__ AND INHERITANCE")
print("=" * 50)
# ============================================================
# PARENT WITH __slots__
# ============================================================
print("\n1. PARENT WITH __slots__")
class Animal:
__slots__ = ['name', 'species']
def __init__(self, name, species):
self.name = name
self.species = species
animal = Animal("Rex", "Dog")
print(f" Animal: {animal.name} ({animal.species})")
# ============================================================
# CHILD WITH __slots__ (Inherits and adds more)
# ============================================================
print("\n2. CHILD WITH __slots__")
class Dog(Animal):
__slots__ = ['breed', 'age'] # Adds more attributes
def __init__(self, name, species, breed, age):
super().__init__(name, species)
self.breed = breed
self.age = age
dog = Dog("Rex", "Canine", "German Shepherd", 3)
print(f" Dog: {dog.name}, {dog.species}, {dog.breed}, {dog.age}")
# ============================================================
# CHILD WITHOUT __slots__ (Gets __dict__)
# ============================================================
print("\n3. CHILD WITHOUT __slots__")
class Cat(Animal):
# No __slots__ defined
def __init__(self, name, species, color):
super().__init__(name, species)
self.color = color
cat = Cat("Whiskers", "Feline", "Orange")
print(f" Cat: {cat.name}, {cat.species}, {cat.color}")
# Cat has __dict__ because it doesn't define __slots__
print(f" Cat __dict__: {cat.__dict__}")
# Can add new attributes (because of __dict__)
cat.toys = ["ball", "mouse"]
print(f" Cat toys: {cat.toys}")
# ============================================================
# CHILD WITH __slots__ AND PARENT WITHOUT
# ============================================================
print("\n4. CHILD WITH __slots__, PARENT WITHOUT")
class Vehicle:
def __init__(self, brand):
self.brand = brand
class Car(Vehicle):
__slots__ = ['model', 'year'] # Child has __slots__
def __init__(self, brand, model, year):
super().__init__(brand)
self.model = model
self.year = year
car = Car("Toyota", "Camry", 2023)
print(f" Car: {car.brand}, {car.model}, {car.year}")
# Parent doesn't have __slots__, so car has __dict__
print(f" Car __dict__: {car.__dict__}")
# ============================================================
# RULES SUMMARY
# ============================================================
print("\n" + "-" * 30)
print("INHERITANCE RULES")
print("-" * 30)
print("""
βββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ
β SCENARIO β RESULT β
βββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ€
β Parent has __slots__ β Child's __slots__ extends parent's β
β Child has __slots__ β Both sets of attributes are allowed β
β β β
β Parent has __slots__ β Child gets a __dict__ (no memory savings) β
β Child has NO __slots__ β β
β β β
β Parent has NO __slots__ β Child can use __slots__ β
β Child has __slots__ β Parent still has __dict__ β
β β β
β Both have __slots__ β Both are memory efficient β
βββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββ
π For full memory savings, use __slots__ in both parent and child.
""")
Inheritance key points:
- Parent has __slots__ β child can extend them
- Parent has __slots__, child doesn't β child gets __dict__
- Parent doesn't have __slots__, child does β child has __slots__, parent still has __dict__
- Both have __slots__ β full memory savings
Quick Check: What happens if a child class doesn't define __slots__ but the parent does? (Answer: The child gets a __dict__, losing the memory savings)
Limitations
What You Can't Do With __slots__
__slots__ is great, but it has some limitations you should know about.
# Limitations of __slots__
print("=" * 50)
print("LIMITATIONS OF __slots__")
print("=" * 50)
# ============================================================
# 1. CAN'T ADD ATTRIBUTES NOT IN __slots__
# ============================================================
print("\n1. CAN'T ADD ATTRIBUTES")
class User:
__slots__ = ['name', 'email']
def __init__(self, name, email):
self.name = name
self.email = email
user = User("Alice", "alice@example.com")
# This works
user.name = "Bob"
# This fails
try:
user.age = 30
except AttributeError as e:
print(f" β Can't add age: {e}")
print(" β οΈ You're limited to the attributes you define in __slots__")
# ============================================================
# 2. CAN'T USE __dict__ (unless added to __slots__)
# ============================================================
print("\n2. CAN'T USE __dict__")
class Point:
__slots__ = ['x', 'y']
def __init__(self, x, y):
self.x = x
self.y = y
p = Point(1, 2)
try:
print(f" __dict__: {p.__dict__}")
except AttributeError as e:
print(f" β No __dict__: {e}")
print(" β οΈ You can add '__dict__' to __slots__ if you need it")
print(" __slots__ = ['x', 'y', '__dict__'] # But this defeats the purpose")
# ============================================================
# 3. CAN'T USE __weakref__ (unless added)
# ============================================================
print("\n3. CAN'T USE __weakref__")
class Data:
__slots__ = ['value']
def __init__(self, value):
self.value = value
d = Data(10)
try:
import weakref
wr = weakref.ref(d)
except TypeError as e:
print(f" β Weakref not supported: {e}")
print(" β οΈ Add '__weakref__' to __slots__ if you need weakrefs")
# ============================================================
# 4. CAN'T USE DEFAULT VALUES IN __slots__ DEFINITION
# ============================================================
print("\n4. CAN'T USE DEFAULT VALUES IN __slots__ DEFINITION")
# β This is wrong
# class Bad:
# __slots__ = ['name', 'age'] # You CAN'T put default values here
# name = "default" # This would be a class variable, not an instance attribute
# β
This is correct
class Good:
__slots__ = ['name', 'age']
def __init__(self, name="default", age=0): # Defaults go in __init__
self.name = name
self.age = age
g = Good()
print(f" Good default: {g.name}, {g.age}")
print(" β
Put default values in __init__, not in __slots__")
# ============================================================
# 5. CAN'T USE __slots__ WITH __dict__ IN SUBCLASSES
# ============================================================
print("\n5. CAN'T USE __slots__ WITH __dict__ IN SUBCLASSES")
class Parent:
__slots__ = ['name']
class Child(Parent):
__slots__ = ['age'] # This works
try:
class BadChild(Parent):
__slots__ = ['age', '__dict__'] # This can cause issues
except Exception as e:
print(f" β Potential issue: {e}")
print(" β οΈ Mixing __slots__ and __dict__ in inheritance can be tricky")
# ============================================================
# SUMMARY
# ============================================================
print("\n" + "-" * 30)
print("LIMITATIONS SUMMARY")
print("-" * 30)
print("""
βββββββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββββ
β LIMITATION β WORKAROUND β
βββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββββββββββββββ€
β Can't add new attributes β Plan your attributes carefully β
β β β
β No __dict__ by default β Add '__dict__' to __slots__ (if needed) β
β β β
β No __weakref__ by default β Add '__weakref__' to __slots__ β
β β β
β No defaults in __slots__ β Use __init__ for defaults β
β β β
β Complex inheritance issues β Keep inheritance simple β
βββββββββββββββββββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββββββββ
π Know the limitations, use __slots__ when it makes sense.
""")
Limitations key points:
- No new attributes β can't add attributes not in __slots__
- No __dict__ β by default, but you can add it
- No __weakref__ β by default, but you can add it
- No defaults β put default values in __init__
- Inheritance complexity β can get tricky with __dict__
Quick Check: Can you add new attributes to an object created from a class with __slots__? (Answer: No β you can only use attributes defined in __slots__)
Real-World Example
Building a Data Processing System
# Real-World Example: Data Processing System
import sys
import random
from time import time
print("=" * 60)
print("DATA PROCESSING SYSTEM")
print("=" * 60)
# ============================================================
# DATA CLASS WITH __slots__ (Memory Efficient)
# ============================================================
class DataRecord:
"""Memory-efficient data record using __slots__"""
__slots__ = ['id', 'name', 'value', 'timestamp', 'active']
def __init__(self, id, name, value, timestamp, active=True):
self.id = id
self.name = name
self.value = value
self.timestamp = timestamp
self.active = active
def process(self):
"""Process the record"""
if self.active:
return self.value * 2
return 0
def get_summary(self):
return f"ID: {self.id}, Name: {self.name}, Value: {self.value}"
# ============================================================
# DATA CLASS WITHOUT __slots__ (For Comparison)
# ============================================================
class DataRecordWithout:
"""Regular class without __slots__"""
def __init__(self, id, name, value, timestamp, active=True):
self.id = id
self.name = name
self.value = value
self.timestamp = timestamp
self.active = active
def process(self):
if self.active:
return self.value * 2
return 0
# ============================================================
# DATA PROCESSOR
# ============================================================
class DataProcessor:
"""Process data records efficiently"""
def __init__(self, use_slots=True):
self.use_slots = use_slots
self.records = []
def generate_data(self, count):
"""Generate random data records"""
self.records = []
for i in range(count):
if self.use_slots:
record = DataRecord(
i,
f"Record_{i}",
random.random() * 100,
time(),
active=random.random() > 0.2
)
else:
record = DataRecordWithout(
i,
f"Record_{i}",
random.random() * 100,
time(),
active=random.random() > 0.2
)
self.records.append(record)
def process_all(self):
"""Process all records"""
results = []
for record in self.records:
results.append(record.process())
return results
def get_summary(self):
"""Get summary of records"""
active = sum(1 for r in self.records if r.active)
total = len(self.records)
return {
"total": total,
"active": active,
"inactive": total - active,
"average_value": sum(r.value for r in self.records) / total if total > 0 else 0
}
# ============================================================
# DEMONSTRATION
# ============================================================
print("\n1. CREATING DATA RECORDS")
# With __slots__
processor_slots = DataProcessor(use_slots=True)
processor_slots.generate_data(10)
print(" Records with __slots__:")
for i, record in enumerate(processor_slots.records[:3]):
print(f" {record.get_summary()}")
print(" ...")
print("\n2. MEMORY COMPARISON")
# Create 10,000 records
count = 10000
processor_slots = DataProcessor(use_slots=True)
processor_slots.generate_data(count)
processor_without = DataProcessor(use_slots=False)
processor_without.generate_data(count)
# Memory usage
mem_slots = sys.getsizeof(processor_slots.records[0]) * count
mem_without = sys.getsizeof(processor_without.records[0]) * count
print(f" Records: {count:,}")
print(f" With __slots__: {mem_slots:,} bytes")
print(f" Without __slots__: {mem_without:,} bytes")
print(f" Memory saved: {mem_without - mem_slots:,} bytes ({((mem_without - mem_slots) / mem_without * 100):.1f}%)")
print("\n3. PERFORMANCE TEST")
import time
# Test with __slots__
processor_slots = DataProcessor(use_slots=True)
processor_slots.generate_data(10000)
start = time.time()
processor_slots.process_all()
time_slots = time.time() - start
# Test without __slots__
processor_without = DataProcessor(use_slots=False)
processor_without.generate_data(10000)
start = time.time()
processor_without.process_all()
time_without = time.time() - start
print(f" With __slots__: {time_slots:.4f}s")
print(f" Without __slots__: {time_without:.4f}s")
print(f" Speed improvement: {((time_without - time_slots) / time_without * 100):.1f}%")
print("\n4. PROCESSING SUMMARY")
summary = processor_slots.get_summary()
print(f" Total records: {summary['total']:,}")
print(f" Active records: {summary['active']:,}")
print(f" Inactive records: {summary['inactive']:,}")
print(f" Average value: {summary['average_value']:.2f}")
print("\n" + "=" * 60)
print("KEY TAKEAWAYS:")
print("=" * 60)
print("β
__slots__ saves memory (40-60% reduction)")
print("β
__slots__ improves performance (10-30% faster)")
print("β
__slots__ prevents accidental attribute creation")
print("β
Use __slots__ for classes with many instances")
print("β
Data processing is a perfect use case for __slots__")
Real-world example key points:
- DataRecord β memory-efficient class with __slots__
- Memory savings β 40-60% less memory
- Speed improvement β 10-30% faster
- Data processing β perfect use case for __slots__
- Scale β even better with more objects
Quick Check: When should you use __slots__? (Answer: When you have many instances of a class and memory matters, like in data processing)
Best Practices
Using __slots__ Effectively
# Best Practices for __slots__
print("=" * 60)
print("BEST PRACTICES FOR __slots__")
print("=" * 60)
# ============================================================
# 1. USE __slots__ FOR CLASSES WITH MANY INSTANCES
# ============================================================
print("\n1. USE __slots__ FOR MANY INSTANCES")
# β
GOOD: For data classes with many instances
class Point:
__slots__ = ['x', 'y', 'z']
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
# β BAD: For classes with few instances
class Config:
# Few instances, __slots__ won't save much memory
def __init__(self, setting1, setting2):
self.setting1 = setting1
self.setting2 = setting2
print(" β
Use __slots__ when you have many objects")
# ============================================================
# 2. USE TUPLES FOR __slots__ (Memory Efficient)
# ============================================================
print("\n2. USE TUPLES FOR __slots__")
# β
GOOD: Use tuple (slightly more memory efficient)
class Good:
__slots__ = ('name', 'age', 'city')
def __init__(self, name, age, city):
self.name = name
self.age = age
self.city = city
# β BAD: Using list (works but uses slightly more memory)
class Bad:
__slots__ = ['name', 'age', 'city'] # Works but less efficient
def __init__(self, name, age, city):
self.name = name
self.age = age
self.city = city
print(" β
Use tuple for __slots__ for better memory efficiency")
# ============================================================
# 3. DON'T MIX __slots__ AND __dict__ UNLESS NECESSARY
# ============================================================
print("\n3. DON'T MIX __slots__ AND __dict__")
# β BAD: Adding __dict__ defeats the purpose
class BadMix:
__slots__ = ['name', 'age', '__dict__'] # Defeats memory savings
def __init__(self, name, age):
self.name = name
self.age = age
# β
GOOD: Keep it clean
class GoodClean:
__slots__ = ['name', 'age']
def __init__(self, name, age):
self.name = name
self.age = age
print(" β
Don't add __dict__ to __slots__ unless you really need it")
# ============================================================
# 4. USE __slots__ IN BOTH PARENT AND CHILD
# ============================================================
print("\n4. USE __slots__ IN BOTH PARENT AND CHILD")
# β
GOOD: Both have __slots__
class Parent:
__slots__ = ['name']
def __init__(self, name):
self.name = name
class Child(Parent):
__slots__ = ['age'] # Child extends parent's slots
def __init__(self, name, age):
super().__init__(name)
self.age = age
# β BAD: Child missing __slots__ loses memory savings
class BadParent:
__slots__ = ['name']
def __init__(self, name):
self.name = name
class BadChild(BadParent):
# No __slots__ β child gets __dict__
def __init__(self, name, age):
super().__init__(name)
self.age = age
print(" β
Use __slots__ in both parent and child for full savings")
# ============================================================
# 5. USE __slots__ FOR IMMUTABLE DATA CLASSES
# ============================================================
print("\n5. USE __slots__ FOR IMMUTABLE DATA")
from dataclasses import dataclass
# β
GOOD: dataclass with frozen=True often uses __slots__ internally
@dataclass(frozen=True)
class ImmutablePoint:
x: int
y: int
# β
GOOD: Manual slots for immutability
class ManualImmutable:
__slots__ = ['_x', '_y']
def __init__(self, x, y):
object.__setattr__(self, '_x', x)
object.__setattr__(self, '_y', y)
@property
def x(self):
return self._x
@property
def y(self):
return self._y
print(" β
__slots__ is great for immutable data classes")
# ============================================================
# 6. SUMMARY
# ============================================================
print("\n" + "=" * 60)
print("BEST PRACTICES SUMMARY")
print("=" * 60)
print("""
βββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββββββ
β PRACTICE β WHY IT MATTERS β
βββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββββββ€
β Use for many instances β Memory savings are significant β
β β β
β Use tuple for __slots__ β Slightly more memory efficient β
β β β
β Don't add __dict__ β Defeats the purpose of __slots__ β
β β β
β Use in parent and child β Full memory savings β
β β β
β Use for immutable data β Combines immutability with efficiency β
β β β
β Use when attributes are β Prevents accidental attribute creation β
β known in advance β β
βββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββββββ
π __slots__ is a powerful tool, but use it when it makes sense!
""")
Best practices summary:
- Use for many instances β memory savings are significant
- Use tuple β slightly more memory efficient
- Don't add __dict__ β defeats the purpose
- Use in parent and child β full memory savings
- Use for immutable data β combines immutability with efficiency
- Use when attributes are known β prevents accidental creation
Quick Check: When should you avoid using __slots__? (Answer: When you need dynamic attributes or when you only have a few instances)
Try It Yourself
Experiment with __slots__ in the editor below.
__slots__ - PRACTICE
==================================================
1. BASIC __slots__ CLASS
Book: Python Programming by Alice Smith (2023)
β Can't add pages: 'Book' object has no attribute 'pages'
2. COMPARE WITH REGULAR CLASS
RegularBook __dict__: {'title': 'Python', 'author': 'Alice', 'year': 2023}
β SlottedBook has no __dict__
RegularBook size: 56 bytes
SlottedBook size: 48 bytes
3. INHERITANCE WITH __slots__
Employee: Alice, ID: E001
β Can't add department: 'Employee' object has no attribute 'department'
You've Got It!
You now understand __slots__ in Python. You know how to save memory, prevent accidental attribute creation, and make your classes more efficient.
Quick Quiz
Test what you've learned:
Frequently Asked Questions
What is __slots__ in Python?
How much memory does __slots__ save?
Can I use __slots__ with dataclasses?
@dataclass(slots=True) to automatically use __slots__. For older versions, you can manually add __slots__ to a dataclass.
Does __slots__ affect inheritance?
Can I add __dict__ to __slots__?
Should I use __slots__ for all my classes?
Where to Go From Here
Now that you understand __slots__ in Python, check out these related topics:
Dataclasses
Learn how dataclasses can use __slots__ for memory efficiency.
Learn More βProperty Decorator
Learn how @property works with __slots__.
Learn More βEnums
Learn about Enums β another way to create efficient classes.
Learn More β