Как работает механизм сокрытия приватных атрибутов и методов в Python (name mangling)?
Что проверяют
Понимаете ли вы, что настоящей приватности в Python нет, и что двойное подчёркивание это не защита доступа, а переименование идентификатора компилятором ради безопасного наследования.
Как отвечать
Правило. Любой идентификатор вида __name (два подчёркивания в начале и не больше одного в конце), встреченный в теле класса, текстово заменяется на _ClassName__name. Ведущие подчёркивания в имени класса при этом отбрасываются.
Когда это происходит. На этапе компиляции, а не в рантайме. Замена применяется и к обращениям внутри методов: self.__x в теле класса A компилируется в self._A__x. Именно поэтому изнутри класса доступ работает, а снаружи даёт AttributeError: снаружи никакой замены не происходит.
class MyClass:
def __init__(self):
self.__value = "приватное"
def get(self):
return self.__value # компилируется в self._MyClass__value
obj = MyClass()
print(obj.get()) # приватное
print(obj.__value) # AttributeError
print(obj._MyClass__value) # приватное, доступ никуда не делся
print(obj.__dict__.keys()) # dict_keys(['_MyClass__value'])Зачем это нужно. Основная цель не сокрытие, а развязка имён при наследовании. Если базовый класс хранит self.__cache, а подкласс заводит своё поле с тем же именем, коллизии не будет: одно станет _Base__cache, другое _Child__cache. То же с методами, поэтому __method нельзя случайно переопределить из подкласса.
Границы механизма:
- Одно подчёркивание
_xне мангится вообще. Это чистое соглашение «внутреннее, руками не трогать», и линтеры его только предупреждают. - Имена вида
__dunder__(подчёркивания с обеих сторон) не мангятся, они зарезервированы под протоколы языка. - Замена работает только по тексту в коде класса. Динамический доступ строкой не мангится:
getattr(obj, "__value")упадёт, нужен полныйgetattr(obj, "_MyClass__value"). - Мангл применяется к любым идентификаторам такого вида внутри тела класса, включая ссылки на глобальные имена, а не только к атрибутам.
Практический вывод. Двойное подчёркивание оправдано в базовых классах и в библиотеках, рассчитанных на наследование. Для обычного «это внутреннее» достаточно одного подчёркивания: код с __ тяжелее отлаживать, мокать в тестах и сериализовать.
Где спотыкаются
Говорят, что двойное подчёркивание делает атрибут приватным и недоступным извне, хотя он доступен как _Class__attr. Вторая ошибка, назвать целью механизма инкапсуляцию и не вспомнить про конфликты имён в иерархии наследования, ради которых он и введён.