Errors and violations

Slotscheck detects several different problems with slots.

Superclass has no slots

In order for memory savings from __slots__ to work fully, all base classes of a slotted classe need to define slots as well:

class NoSlots:

class HasSlots:
    __slots__ = ()

class Bad(NoSlots):
    __slots__ = ('x', 'y')

class Good(HasSlots):
    __slots__ = ('x', 'y')

You can see the memory impact of this mistake with pympler library:

from pympler.asizeof import asizeof

asizeof(Bad())  # 168
asizeof(Good())  # 48

In addition, if a superclass has no slots, all subclasses will get __dict__, which allows setting of arbitrary attributes.

Bad().foo = 5  # no error, even though `foo` is not a slot!
Good().foo = 3  # raises AttributeError, as it should.

Overlapping slots

If a class defines a slot also present in a base class, the slot in the base class becomes inaccessible. This can cause surprising behavior. What’s worse: these inaccessible slots do take up space in memory!

class Base:
    __slots__ = ('x', )

class Bad(Base):
    __slots__ = ('x', 'z')

class Good(Base):
    __slots__ = ('z', )

The official Python docs has this to say about overlapping slots (emphasis mine):

If a class defines a slot also defined in a base class, the instance variable defined by the base class slot is inaccessible (except by retrieving its descriptor directly from the base class). This renders the meaning of the program undefined. In the future, a check may be added to prevent this.

Duplicate slots

Python doesn’t stop you from declaring the same slot multiple times. This mistake will cost you some memory:

class Good:
    __slots__ = ('a', 'b')

 class Bad:
    __slots__ = ('a', 'a', 'a', 'b', 'a', 'b')

 asizeof(Good())  # 48
 asizeof(Bad())  # 80