Introduction to Python's slots

Here we are going to look into the easy way to speed up your Python code with \_\_slots\_\_ and its technical implementation details.
This page is laregly all notes from the medium article "A quick dive into Python's "\_\_slots\_\_" " by Stephen Jayakar .

## What is it?

\_\_slots\_\_ is an attribute you can add to a Python class when defining it. You define slots with the possible attributes that an instance of an object can possess.
It is a special attribute that allows you to explicitly state which instance attributes you expect your object instances to have.

Here is an example of how you use it :

```
class WithSlots:
    __slots__ = ('x', 'y')

    def __init__(self, x, y):
        self.x, self.y = x, y

```

For instances of this class, you can use self.x and self.y in the same ways as a normal class instance. However, one key difference between this and instancing from a normal class is that you cannot add or remove attributes from this class's instances.

Say the instance was called w : you couldn't write w.z = 2 without causing an error.

## Why use it?

The biggest higher-level reasons to use \_\_slots\_\_ are :

1. **Faster attribute getting and setting** due to data structure optimization.
2. **Reduced memory usage** for class instances.

The memory saving comes from storing value references in slots instead of \_\_dict\_\_ , and denying \_\_dict\_\_ and \_\_weakfref\_\_ creation if parent classes deny them and you declare \_\_slots\_\_ .

## Why you shouldn't use it

You wouldn't want to use it if your class has attributes that change during run-time (dynamic attributes) or if there's a complicated object inheritance tree.

## Examples

Lets look at some examples of when \_\_slots\_\_ is faster, starting with mass instanciation. Using Python's "timeit" module and this code snippet, we get the following :

```
        self.x = x
        self.y = y
        self.z = z

class WithSlots:
    __slots__ = ('x', 'y', 'z')

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

        x = cls(1, 2, 3)
    return instance

```

```
Without Slots: 0.3909880230203271
With Slots: 0.31494391383603215
(averaged over 100000 iterations)

```

In general, instantiation time is not really improved by using \_\_slots\_\_ .
Despite not having to create \_\_dict\_\_ , there's other overhead that needs to be done with slots that we'll go into later, which results in a similar runtime to copying over the dictionary from the actual class.

The real speedup comes into play when we start getting and setting values in rapid succession :

```
def get_set_fn(cls):
    x = cls(list(range(26)))

    def get_set():
        x.y = x.z + 1
        x.a = x.b - 1
        x.d = x.q + 3
        x.i = x.j - 1
        x.z = x.y / 2

    return get_set

```

```
Without Slots: 11.59717286285013
With Slots: 9.243316248897463
(averaged over 100000 iterations)

```

Thats a huge speed increase!
Here is another quick example of using and not using slots :

```
# Without Slots :
        self.name = name
        self.identifier = identifier

# With Slots :
class WithSlots(object):
    __slots__ = ['name', 'identifier']
    def __init__(self, name, identifier):
        self.name = name
        self.identifier = identifier

x = WithSlots("My Name", "My Identifier")
print(x.name)
print(x.identifier)

```

```
My Name
My Identifier
[Finished in 0.1s]

```

Now say if we wrote x.thing = "New Thing" , we would get the error :

```
AttributeError: 'WithSlots' object has no attribute 'thing'

```

However if we called the same thing but using the WithoutSlots class, we would not get this error :

```
x = WithoutSlots("My Name", "My Identifier")

x.thing = "New Thing"

```

No attribute error produced here.

## Conslusion

In Python, every class can have instance attributes, By default Python uses dict to store an object's instance attributes. This is really helpful as it allows setting new attributes at run-time.

However, for small classes with known attributes it might be a bottleneck. The dict wastes a lot of RAM. Python can't allocate a static amount of memory at object creation to store all the attributes.

Therefore it sucks a lot of RAM if you create a lof of objects (this would need to be A LOT). Still there is a way to get around this issue, it involves using \_\_slots\_\_ to tell Python not to use a dict, and only allocate space for a fixed set of attributes.

## References

A quick dive into Python's "\_\_slots\_\_"  by Stephen Jayakar .

Usage of \_\_slots\_\_? - Stackoverflow

\_\_slots\_\_ Magic - Python Tips