Ответ
Двойное подчеркивание (__) в начале имени атрибута класса (например, __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': 'Сыночково'}
Вот, блядь, и вся фишка! Два разных атрибута, живут не мешая друг другу. Не конфликт, а мир, дружба, жвачка. Искажение имён — оно чтобы вот такие вот распиздяйства не случались, когда пишешь код на коленке в три часа ночи.