How and when to use __slots__ in python

· 3 min read April 26, 2022

banner

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 :


# 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 .

     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

     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.

    
    # 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 .

    
    #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 ..

Some Thing to say ?