__slots__ tells the interpreter not to use a dict, and only allocate space for a fixed set of attributes.

Every class have instance attributes, no matter what kind of programming language is. By default, Python uses a dict(__dict__) to store instance attributes of an object, which allows you to set arbitrary new attributes at runtime handily.

class Bird(object):
    fly = True
    def __init__(self, age):
        self.age = age

print Bird.__dict # {'fly': True, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Bird' objects>, '__weakref__': <attribute '__weakref__' of 'Bird' objects>, '__doc__': None, '__init__': <function __init__ at 0x1052cbf50>}

larry = Bird(2)
print larry.__dict__ # {'age': 2}

larry.__dict__['shoot'] = True
print larry.__dict__ # {'age': 2, 'shoot': True}

Bird.__dict__['three'] = 'excellent'  # TypeError: 'dictproxy' object does not support item assignment

Nevertheless, the __dict__ is not versatile, it wastes memory. Python can’t just allocate a static amount of memory at object creation to store all the attributes, since more may be added. __slots__ tells interpreter not to use a dict, and only allocate space for a fixed set of attributes.

class Penny(object):
    __slots__ = ['shoot', 'assist']
    def __init__(self, shoot, assist):
        self.shoot = shoot
        self.assist = assist

# {'shoot': <member 'shoot' of 'Penny' objects>, '__module__': '__main__', 'assist': <member 'assist' of 'Penny' objects>, '__slots__': ['shoot', 'assist'], '__doc__': None, '__init__': <function __init__ at 0x105376c08>}
print Penny.__dict__

p = Penny(True, 'top')
print p.__dict__ # AttributeError: 'Penny' object has no attribute '__dict__'

Without a __dict__ variable, instances cannot be assigned new variables not listed in the __slots__ definition. Attempts to assign to an unlisted variable name raise an AttributeError.

Without a __weakref__ variable for each instance, classes defining __slots__ do not support weak references to its instances. If weak reference support is needed, then add __weakref__ to the sequence of strings in the __slots__ declaration. Likewise, add __dict__’ to __slots__, so you can get dynamic assignment.

from weakref import ref

class A(object):
    __slots__ = ['b']
    def __init__(self):
        self.b = 1


class B(object):
    __slots__ = ['b', '__weakref__']
    def __init__(self):
        self.b = 1

a = A()
r = ref(a) # TypeError: cannot create weak reference to 'A' object

b = B()
r = ref(b) 
r # <weakref at 0x10468ff70; to 'type' at 0x7fde5d978430 (B)>

What will it be like if we involve inheritance? Let’s try:

class Base(object):
    __slots__ = ['desc']
    def __init__(self):
        self.desc = "Base"

class Derived(Base):
    def __init__(self):
        pass

d = Derived()
d.__dict__ # {}
d.__slots__ # ['desc']

As you can see, you don’t even need to declare __slots__ in your subclass, and you will still use slots from the parents, but not restrict the creation of a __dict__.

How about multiple inheritance?

class BaseA(object): __slots__ = ['a']
class BaseB(object): __slots__ = ['b']

# TypeError: Error when calling the metaclass bases
#    multiple bases have instance lay-out conflict
class Derived(BaseA, BaseB): pass

Using an empty __slots__ in the parent seems to provide the most flexibility, allowing the child to choose to prevent or allow (by adding __dict__ to get dynamic assignment, see section above) the creation of a __dict__.

class BaseA(object): __slots__ = []
class BaseB(object): __slots__ = []
class Derived(BaseA, BaseB): __slots__ = ['a', 'b']