Какую роль выполняет двойное подчеркивание (__) в именах атрибутов класса в Python

Ответ

Двойное подчеркивание (__) в начале имени атрибута класса (например, __my_var) активирует механизм искажения имен (name mangling).

Его основная цель — избежать случайного переопределения атрибутов в дочерних классах, а не создание по-настоящему приватных переменных.

Как это работает?

Интерпретатор Python автоматически изменяет имя такого атрибута, добавляя к нему префикс _ИмяКласса. Таким образом, атрибут __value в классе MyClass станет _MyClass__value.

Пример без наследования:

class MyClass:
    def __init__(self):
        self.__private_attr = 42

obj = MyClass()

# Прямой доступ вызовет ошибку
# print(obj.__private_attr)  # AttributeError

# Доступ через искаженное имя работает
print(obj._MyClass__private_attr) # Вывод: 42

Пример с наследованием (демонстрация основной цели):

Этот механизм предотвращает коллизии, если дочерний класс определяет атрибут с таким же именем.

class Parent:
    def __init__(self):
        self.__value = "Parent Value" # Преобразуется в _Parent__value

    def get_value(self):
        return self.__value

class Child(Parent):
    def __init__(self):
        super().__init__()
        self.__value = "Child Value" # Преобразуется в _Child__value

    def get_child_value(self):
        return self.__value

c = Child()
print(c.get_value())        # Вывод: Parent Value
print(c.get_child_value())  # Вывод: Child Value
print(c.__dict__)           # {'_Parent__value': 'Parent Value', '_Child__value': 'Child Value'}

Как видно из примера, атрибуты __value в родительском и дочернем классах не конфликтуют, так как были преобразованы в разные имена.

Ответ 18+ 🔞

А, ну это же про эти ваши двойные подчёркивания! Слушай, народ часто нихуя не понимает, что это за магия такая — __my_var. Думают, типа, "ой, приватность, сокрытие, капсуляция, блядь!". А на самом деле, это просто такой костыль, чтобы в наследниках случайно не перетереть атрибут, который в родителе уже есть. Называется name mangling, или, по-нашему, искажение имён.

Как оно работает, этот цирк?

Пайтон берёт и тупо переименовывает атрибут, приклеивая спереди _ИмяКласса. Вот и вся магия. Не приватность, а просто чтобы не обосраться при наследовании.

Смотри, без наследников-то:

class MyClass:
    def __init__(self):
        self.__secret = 1337  # Станет _MyClass__secret

obj = MyClass()

# Попробуй напрямую — получишь по ебалу
# print(obj.__secret)  # AttributeError: уёбок не найдено

# А вот так — пожалуйста, нате
print(obj._MyClass__secret) # Вывод: 1337

Видишь? Никакой приватности, просто имя поменяли. Хочешь — лезь.

А вот где реальный смысл, так это когда классы плодятся:

Вот смотри, чтобы в ребёнке случайно не наступить на те же грабли, что и в родителе.

class Papa:
    def __init__(self):
        self.__data = "Папино"  # Станет _Papa__data

    def show_data(self):
        return self.__data

class Synok(Papa):
    def __init__(self):
        super().__init__()
        self.__data = "Сыночково"  # А это уже _Synok__data

    def show_synok_data(self):
        return self.__data

s = Synok()
print(s.show_data())         # Вывод: Папино
print(s.show_synok_data())   # Вывод: Сыночково
print(s.__dict__)            # {'_Papa__data': 'Папино', '_Synok__data': 'Сыночково'}

Вот, блядь, и вся фишка! Два разных атрибута, живут не мешая друг другу. Не конфликт, а мир, дружба, жвачка. Искажение имён — оно чтобы вот такие вот распиздяйства не случались, когда пишешь код на коленке в три часа ночи.