Python is known for its flexibility and dynamic nature, but this flexibility comes at a cost: memory consumption. When you create a Python class instance, it typically stores attributes in a dictionary. This dictionary allows for dynamic attribute addition, but it also incurs overhead in terms of memory and lookup time. This is where __slots__
comes in.
__slots__
is a special class attribute that allows you to explicitly define the attributes that an instance of your class can have. By doing so, you effectively trade the flexibility of dynamic attribute addition for significant memory savings and potentially faster attribute access.
How __slots__
Works
Instead of using a dictionary to store attributes, Python uses a tuple or a fixed-size array when __slots__
is defined. This means attribute access becomes faster and, more importantly, consumes less memory, especially when dealing with a large number of instances.
Basic Example
Let’s illustrate the difference with a simple example:
import sys
class MyClass:
def __init__(self, name, age):
self.name = name
self.age = age
class MySlotClass:
__slots__ = ['name', 'age']
def __init__(self, name, age):
self.name = name
self.age = age
= MyClass("Alice", 30)
instance1 = MySlotClass("Bob", 25)
instance2
print(f"Size of MyClass instance: {sys.getsizeof(instance1)} bytes")
print(f"Size of MySlotClass instance: {sys.getsizeof(instance2)} bytes")
You’ll observe that the MySlotClass
instance (using __slots__
) is significantly smaller than the MyClass
instance. The difference becomes more pronounced as the number of attributes and instances increases.
Adding Attributes After Instance Creation
One crucial limitation of __slots__
is that you cannot add new attributes to instances after creation. Attempting to do so will raise an AttributeError
.
= "New York" # This will raise an AttributeError instance2.city
Inheritance and __slots__
Inheritance with __slots__
requires careful consideration. Subclasses inheriting from a class with __slots__
must explicitly define their own __slots__
, listing all attributes from the parent class and any new attributes. Failing to do so will allow dynamic attribute assignment in the subclass, negating the memory benefits of __slots__
in the parent class.
class ParentSlotClass:
__slots__ = ['name']
def __init__(self, name):
self.name = name
class ChildSlotClass(ParentSlotClass):
__slots__ = ['name', 'age'] # Must include 'name' from parent
def __init__(self, name, age):
super().__init__(name)
self.age = age
When to Use __slots__
__slots__
are particularly beneficial in scenarios where:
- You have a large number of instances of a class.
- Memory usage is a critical concern.
- You need a slight performance boost in attribute access.
- You want to enforce a fixed set of attributes for a class, preventing accidental attribute additions.
However, remember that __slots__
reduces flexibility. If you anticipate needing dynamic attribute addition, then __slots__
is not the right choice.