It's methods like ``__init__`` or ``__eq__`` that are sometimes also called *magic methods* or it's said that they implement an *object protocol*.
In spoken form, you'd call ``__init__`` just "dunder init".
Its first documented use is a `mailing list posting <https://mail.python.org/pipermail/python-list/2002-September/155836.html>`_ by Mark Jackson from 2002.
This is quite wasteful especially for objects with very few data attributes and the space consumption can become significant when creating large numbers of instances.
This is the type of class you get by default both with and without ``attrs`` (except with the next APIs `attrs.define()`, `attrs.mutable()`, and `attrs.frozen()`).
A class whose instances have no `object.__dict__` attribute and `define <https://docs.python.org/3/reference/datamodel.html#slots>`_ their attributes in a `object.__slots__` attribute instead.
In ``attrs``, they are created by passing ``slots=True`` to ``@attr.s`` (and are on by default in `attrs.define()`/`attrs.mutable()`/`attrs.frozen()`).
- However, `it's not possible <https://docs.python.org/3/reference/datamodel.html#notes-on-using-slots>`_ to inherit from more than one class that has attributes in ``__slots__`` (you will get an ``TypeError: multiple bases have instance lay-out conflict``).
- It's not possible to monkeypatch methods on slotted classes.
This can feel limiting in test code, however the need to monkeypatch your own classes is usually a design smell.
If you really need to monkeypatch an instance in your tests, but don't want to give up on the advantages of slotted classes in production code, you can always subclass a slotted class as a dict class with no further changes and all the limitations go away:
- Slotted classes must implement :meth:`__getstate__ <object.__getstate__>` and :meth:`__setstate__ <object.__setstate__>` to be serializable with `pickle` protocol 0 and 1.
If the ``@attr.s(slots=True)`` decorated class already implements the :meth:`__getstate__ <object.__getstate__>` and :meth:`__setstate__ <object.__setstate__>` methods, they will be *overwritten* by ``attrs`` autogenerated implementation by default.
This can be avoided by setting ``@attr.s(getstate_setstate=False)`` or by setting ``@attr.s(auto_detect=True)``.
- The `class.__subclasses__` attribute needs a garbage collection run (which can be manually triggered using `gc.collect`), for the original class to be removed.
See issue `#407 <https://github.com/python-attrs/attrs/issues/407>`_ for more details.