Slots
Each instance normally keeps its attributes in a dict, obj.__dict__. That is flexible, and it costs memory for every object. Listing the attribute names in __slots__ gives instances fixed storage instead.
class Plain:
def __init__(self, x):
self.x = x
class Slim:
__slots__ = ("x",)
def __init__(self, x):
self.x = x
p, s = Plain(1), Slim(1)
p.typo = 2 # silently accepted
print(hasattr(s, "__dict__"))
try:
s.typo = 2
except AttributeError as e:
print(e)What changes
- Assigning a name that is not in
__slots__raisesAttributeError, which catches misspellings such asself.raduis. - Instances are smaller and attribute access is a little faster. It matters when you create many thousands of objects.
- A subclass without its own
__slots__gets a__dict__back. Give each subclass__slots__listing only its new names. - A slotted name cannot also have a class-level default. Set defaults in
__init__.
@dataclass(slots=True) writes __slots__ for you.
Your task
Give Vector(x, y) __slots__ so instances have no __dict__ and reject unknown attributes. Make Vector3(Vector), which adds z defaulting to 0, keep the same restriction by listing only its new name in its own __slots__.
The explanation opens here once every check passes.