__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']