Какие модификаторы доступа существуют в Python и в чем их разница?
Что проверяют
Знаете ли вы, что в Python нет модификаторов доступа в смысле Java, и понимаете ли, что на самом деле делают одно и два подчёркивания. Следом почти всегда просят объяснить name mangling и показать, как всё же достать «приватный» атрибут снаружи.
Как отвечать
Сразу скажите главное: интерпретатор не запрещает доступ ни к какому атрибуту. Есть соглашения и один механизм переименования.
name: публичный интерфейс класса или модуля;_name: внутреннее, «не трогайте, если не знаете, что делаете». Это только соглашение: доступ снаружи возможен, линтер pylint предупреждает о нём (protected-access). На уровне модуляfrom module import *пропускает такие имена, если не задан__all__;__name: включает name mangling. Внутри классаBaseимяself.__tokenпревращается вself._Base__token. Цель не приватность, а защита от случайного конфликта имён с наследниками: подкласс, объявивший свой__token, получит_Child__tokenи не затрёт родительский;__name__: dunder-методы и атрибуты, их не переименовывают. Свои имена такого вида придумывать не стоит.
Покажите, как получают контролируемый доступ: @property с сеттером для проверки значения, __slots__ для фиксированного набора атрибутов. Для статической проверки есть typing.Final и декоратор @final, их соблюдение проверяют mypy или pyright, но не рантайм.
Хорошая формулировка для финала: в Python инкапсуляция держится на договорённости («мы все здесь взрослые люди») и на документации.
Где спотыкаются
Говорят, что _name доступен только классу и наследникам. Никаких ограничений нет, это подсказка для людей и инструментов.
Ставят __name везде как замену private. Это усложняет наследование и отладку, а доступ всё равно остаётся через obj._Base__name.
Считают, что подкласс не может добраться до __name родителя. Через self.__name не может, потому что имя переименуется под подкласс, но через полное имя self._Base__name доступ есть.