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:
pass
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