Python’s Slots and Customizing Class Creation

Slots are essentially predefined attribute names for your classes. They allow you to create more efficient objects by replacing instance dictionaries with fixed-length arrays, which in turn provides immediate detection of bugs due to misspelled attribute assignments. Lets dive into some examples and see how we can use slots to customize class creation!

To kick things off let’s say we have a base class that defines some common functionality for our objects:

# Defining a base class with slots to customize class creation
class BaseClass:
    # Initializing the class with a parameter x
    def __init__(self, x):
        self.x = x
        
    # Defining a property y that returns 2 times the value of x
    @property
    def y(self):
        return 2 * self.x
    
    # Defining a method do_something that prints a message
    def do_something(self):
        print("Base class doing something!")

Now let’s say we want to create a subclass that inherits from `BaseClass`, but also adds some new functionality:

# Creating a subclass that inherits from BaseClass and adds new functionality

# Define the SubClass that inherits from BaseClass
class SubClass(BaseClass):
    # Define slots for our class
    __slots__ = ('z')

    # Initialize the SubClass with parameters x and y
    def __init__(self, x, y):
        # Call the __init__ method of the parent class using super()
        super().__init__(x)
        # Set the value of y for the SubClass
        self.y = y

    # Create a property for z that calculates its value based on x and y
    @property
    def z(self):
        return 3 * self.x + self.y

    # Define a method for the SubClass to do something else
    def do_something_else(self):
        print("Sub class doing something else!")

In this example, we’ve defined a new slot `z`, which is used to store the result of our custom property method. By defining slots in our subclass, we can save memory and improve performance by avoiding the creation of unnecessary dictionary keys for attributes that are not being used.

But what if we want to create multiple inheritance with slotted parent classes? Well, you’re in luck! Python allows us to do this as long as only one parent class has non-empty slots violations raise a `TypeError`. Let’s see an example:

# Define our first slotted parent class
class ParentClass1(metaclass=type):
    __slots__ = ('a', 'b') # define slots for attributes 'a' and 'b'
    
    # Define a new instance of the class
    def __new__(cls, *args, **kwargs):
        obj = super().__new__(cls) # create new instance of the class
        for slot_name in cls.__dict__.get('slot_names', []):
            setattr(obj, slot_name, None) # initialize slots with default values
        
        return obj
    
    # Initialize the instance with values for attributes 'a' and 'b'
    def __init__(self, a, b):
        self.a = a
        self.b = b
        
# Define our second parent class without slots
class ParentClass2:
    pass
    
# Inherit from both slotted and non-slotted parents
class SubClass(ParentClass1, ParentClass2):
    __slots__ = ('c') # define new slot for subclass

In this example, we’ve defined a metaclass `type` to initialize slots with default values in our first parent class. This allows us to avoid creating unnecessary dictionary keys when instantiating objects of that class. By inheriting from both slotted and non-slotted parents, we can customize the behavior of our subclass while still maintaining efficient memory usage.

They allow us to create more efficient objects by replacing instance dictionaries with fixed-length arrays, which in turn provides immediate detection of bugs due to misspelled attribute assignments. Give them a try and see how they can improve the performance of your classes!

SICORPS