How and when to use __slots__ in python
Tags: python, programming, beginners
Published: 2022-05-05 05:33:18



Each python object has a \__dict__ attribute which is a dictionary containing
all other attributes. E.g. when you type self.attr python is actually doing
self.__dict__['attr'].

As you can imagine using a dictionary to store attribute takes some extra space
& time for accessing it.

What if you already knew which attributes your class is going to have . Then
using \__dict__ to store attributes doesn't  seem to be a good idea as we don't
need the dynamic nature of dicts .

This is where slots come to rescue .


# What are __slots__ in python

\__slots__ is a class variable which allows us declare  the  attributes  of a
class explicitly and denying the creation of \__dict__ and
\__weakref__ for object instances .

The expected attributes can be assigned to \__slots__  as a string, iterable,
or sequence of strings with variable names used by instance .

**Space saved by  not having a \__dict__  can be significant  . Slots also
improve the attribute lookup speed .**



# Usage of __slots__

Python utilises a considerably more compact internal representation for
instances when you define \__slots__. Instead of a dictionary, each instance
is created around a small fixed-sized array, similar to a tuple or list.

As a result of using slots, you can **no longer add new attributes to
instances**;
you are limited to the attribute names given in the \__slots__ specifier.( You
can get around this by adding \__dict__ to the \__slot__ . The dict will only
initialized when a dynamic attribute is added ) .

Example showing usage of slots :


``` python

# A email class with out using slots

class Email :
  def __init__(self,subject,to,message) :
    self.subject = subject
    self.message = message
    self.to = to


class EmailWithSlots :

  __slots__ = ('subject','to','message')

  def __init__(self,subject,to,message) :
    self.subject = subject
    self.message = message
    self.to = to


email = EmailWithSlots('test','me@gmail.com','testing slots')

email.subject
# >> test

email.__dict__  # cant access __dict__ because its not created


# AttributeError                            Traceback (most recent call last)
# <ipython-input-40-b95667a7fb92> in <module>
#  ----> 1 email.__dict__
#
#AttributeError: 'EmailWithSlots' object has no attribute '__dict__'

email.from = "aabid@gmail.com" # cant add an atribute that not in __slots__

# ---------------------------------------------------------------------------
# AttributeError                            Traceback (most recent call last)
# <ipython-input-42-87651e4df821> in <module>
# ----> email.from = "aabid@gmail.com"
#
#AttributeError: 'EmailWithSlots' object has no attribute 'from'



```


# Pros And Cons  of __slots__


## Pros

- using \__slots__ has faster attribute access .
- \__slots__  reduces memory usage


## Cons

- Fixed attributes ( You can get around this by adding \__dict__ to the
     \__slots__ . The dict will only initialized when a dynamic attribute is added )
-  \__slots__ are implemented at the class level by creating descriptors
     for each variable name. As a result, class attributes cannot be used to set
     default values for instance variables defined by \__slots__; otherwise, the
     class attribute would overwrite the descriptor assignment.



# Inheritance with __slots__  .

Working with slots and Inheritance is a little tricky and need to be taken care
of .  Here is How :

- The \__slots__ defined on parent class are automatically accessible to the
    child .

  ```python
   class BaseClass :
       __slots__ = ['x','y','z']


   class Inherited(BaseClass) :
        pass


   Inherited.__slots__

   #>> ['x', 'y', 'z']

   ```
- If the subclass doesn't specify slots ( empty or new ) . The subclass will
    get \__dict__ and \__weakref__ attributes in addition to slots from parent


  ```python
   class BaseClass :
       __slots__ = ['x','y','z']


   class Inherited(BaseClass) :
        pass

   class InheritedWithSlots(BaseClass) :
      __slots__ = ()

   Inherited.__slots__
   #>> ['x', 'y', 'z']

   Inherited.__dict__
   #>> {}

   InheritedWithSlots().__dict__
   # AttributeError

   ```

-  Nonempty \__slots__ does not work for classes derived from “variable-length” built-in types such as int, bytes and tuple.

    ```python

    # this works fine because we are not using any additional slots in subclass
    class MyInt(int):
        __slots__ = ()


    # This will panic because we are adding non-empty slots .
    class NewInt(int) :
        __slots__ = (x,y)

    #TypeError: nonempty __slots__ not supported for subtype of 'int'

    ```
- You can also use multiple inheritance with slots . But only one parent can
    have non empty \__slots__. Otherwise a typeerror is raised .

    ```python

    #lets have three slotted base classes

    class foo:
      __slots__ = ("x","y")

    class bar :
      __slots__ = ("a","b")

    class baz :
       __slots__=()


    # this will raise TypeError as we are inheriting from two classes
    # with nonempty slots
    class foobar(foo,bar) :
      pass

    #>>TypeError: multiple bases have instance lay-out conflict


    # This shall work as only one of the inherited class has nonempty slots

    class FooBaz(foo,bar) :
      pass


    ```




# Conclusion :


\__slots__ allow us to explicitly state which instance variables to expect in
the object

Which gives us following benefits

1. Faster Attribute Access
2. Saves Memory


## A typical use case

For classes that primarily serve as simple data structures, you can often greatly reduce
the memory footprint of instances by adding the \__slots__ attribute to the
class definition.

## When __slots__ is a bad idea .

- Avoid them when you want to perform \__class__ assignment with another class that doesn't have them (and you can't add them) unless the slot layouts are identical. (I am very interested in learning who is doing this and why.)

- Avoid them if you want to subclass variable length builtins like long, tuple, or str, and you want to add attributes to them.

- Avoid them if you insist on providing default values via class attributes for instance variables.


**Thanks , for reading till end Happy coding ..**