Dunder‑методы позволяют «вписать» свой класс в протоколы языка:
__call__ — «что делать, когда объект вызывают как функцию»;
__enter__ / __exit__ — «как вести себя в with»;
__getattr__ / __setattr__ — «как обрабатывать доступ к атрибутам».
Метод __call__
Если в классе есть метод __call__, экземпляр этого класса можно вызвать как функцию: obj(). Python при этом делает obj.__call__(...).
Типичные применения: объекты‑функции (настраиваемые обработчики, валидаторы, callback‑объекты), а также объекты, которые нужно конфигурировать в __init__, а потом много раз «запускать» одним и тем же способом.
Методы __enter__ и __exit__
Эти методы делают объект контекстным менеджером и используются конструкцией with. При входе в with Python вызывает __enter__ и то, что он вернёт, попадает в as ...; При выходе (нормальном или с исключением) вызывается __exit__(exc_type, exc, tb) для очистки ресурсов. Используются для файлов, соединений с БД, сетевых сокетов, локов — всё, что надо гарантированно закрыть/освободить.
__getattr__ и __setattr__
Оба регулируют доступ к атрибутам.
__getattr__(self, name) вызывается, когда обычный поиск атрибута не нашёл ничего (нет в __dict__ экземпляра и классов). Это «план Б» на случай «атрибут не найден». Можно использовать для ленивых вычислений, прокси, динамической генерации атрибутов.
__setattr__(self, name, value) вызывается при любом присваивании self.name = value. Через него можно контролировать, логировать, валидировать все записи атрибутов.
Обычно их используют аккуратно, чтобы не превратить класс в «магический чёрный ящик», но они хорошо подходят для логгеров, ORM‑подобных штук, прокси‑объектов.
Как можно ответить на собеседовании?
Это всё примеры dunder‑методов — специальных методов, которые позволяют классу участвовать в протоколах языка. __call__ делает объект вызываемым, __enter__ / __exit__ позволяют использовать его в with как контекстный менеджер, а __getattr__ и __setattr__ дают контроль над динамическим доступом и записью атрибутов. Через такие методы Python связывает твой класс с синтаксисом языка: вызов как функции, менеджеры контекста, доступ к атрибутам, операторы и так далее.