Каково назначение префиксов _ и __ в именах атрибутов и методов в Python

Ответ

В Python префиксы с подчеркиванием используются для управления видимостью и поведением имен атрибутов и методов класса, следуя соглашениям и задействуя встроенные механизмы языка.

1. Одиночное подчеркивание (_)

Это соглашение (convention), которое сигнализирует другим разработчикам, что атрибут или метод предназначен для внутреннего использования внутри класса или модуля. Интерпретатор Python никак не ограничивает доступ к таким именам, но они не импортируются при использовании from module import *.

Назначение:

  • Подсказать, что это внутренняя деталь реализации, которую не следует использовать извне.
  • Избежать засорения пространства имен при импорте со звездочкой.

Пример:

class MyWidget:
    def __init__(self):
        self.public_data = 10
        self._internal_data = 20 # Считается внутренним

    def _helper_method(self):
        # Внутренний вспомогательный метод
        return "Helper result"

w = MyWidget()
print(w.public_data)       # OK
print(w._internal_data)    # Технически возможно, но не рекомендуется

2. Двойное подчеркивание (__)

Это не просто соглашение, а запуск механизма искажения имен (name mangling). Python автоматически изменяет имя атрибута, добавляя к нему префикс _ИмяКласса.

Назначение:

  • Избежать случайного переопределения (конфликта имен) в дочерних классах.

Пример:

class Parent:
    def __init__(self):
        self.__private_var = "I am from Parent"

    def get_var(self):
        return self.__private_var

class Child(Parent):
    def __init__(self):
        super().__init__()
        self.__private_var = "I am from Child"

obj = Child()
print(obj.get_var()) # Выведет: "I am from Parent"

# Прямой доступ невозможен
# print(obj.__private_var) # Вызовет AttributeError

# Доступ через искаженное имя все еще возможен
print(obj._Parent__private_var) # Выведет: "I am from Parent"
print(obj._Child__private_var)  # Выведет: "I am from Child"

Ключевое различие: _ — это 'подсказка' для программиста, а __ — механизм, предотвращающий случайное переопределение в дочерних классах.

Ответ 18+ 🔞

А, ну это ж классика, блядь! Про эти подчёркивания в питоне! Сейчас объясню, как есть, без соплей.

Смотри, есть у нас два главных героя: одинокое подчёркивание _ и двойное __. И они, сука, делают разную работу, хоть и выглядят похоже.

Одинокое подчёркивание (_) — это типа вежливая просьба

Это не железное правило языка, а такой джентльменский договор, ёпта. Ты как бы говоришь другим разработчикам: «Эй, мужики, это моё внутреннее хозяйство, не лезьте сюда, а то мало ли что». Сам питон на это подчёркивание по большому счёту похуй — доступ он не закрывает. Но при импорте звёздочкой (from module import *) такие имена не подтянутся, и это хорошо.

Зачем нужно?

  • Чтобы не засрать пространство имён своими внутренними костылями.
  • Чтобы твои коллеги не начали использовать твой кривой костыльный метод, а потом ты его поменяешь и всё сломается.

Пример, смотри:

class МояХрень:
    def __init__(self):
        self.публичная_цифирь = 100  # Бери, пользуйся
        self._внутренний_костыль = 999  # Эй, это моё, руками не трогай!

    def _костыльный_метод(self):
        # Тут такая дичь творится, что лучше не знать
        return "Результат костыля"

объект = МояХрень()
print(объект.публичная_цифирь)    # Ну ок, 100
print(объект._внутренний_костыль) # Технически даст 999, но ты же не мудак, чтобы так делать?

Двойное подчёркивание (__) — это уже серьёзно, тут питон вмешивается

Вот это уже не просьба, а механизм, блядь! Называется «name mangling», или, по-нашему, «изнасилование имени». Питон берёт имя атрибута и прикручивает к нему спереди _ИмяКласса, чтобы в дочерних классах не было конфуза.

Зачем это, нахуй?

  • Чтобы твой «приватный» атрибут в классе-наследнике случайно не перекрыли таким же именем. Это защита от дурака, то есть от тебя же в будущем.

Сейчас будет магия, смотри не обосрись:

class Родитель:
    def __init__(self):
        self.__мой_секрет = "Я из Родителя"  # Двойное подчёркивание, ёба!

    def показать_секрет(self):
        return self.__мой_секрет

class Ребёнок(Родитель):
    def __init__(self):
        super().__init__()
        self.__мой_секрет = "А я из Ребёнка!"  # Думаешь, перезатёр? Хуй там!

объект = Ребёнок()
print(объект.показать_секрет()) # Выведет: "Я из Родителя". Ничего не сломалось!

# А вот так уже не выйдет, будет ошибка:
# print(объект.__мой_секрет) # AttributeError: пиздец

# Но если ты хитрожопый и знаешь, как питон изнасиловал имя, то докопаешься:
print(объект._Родитель__мой_секрет) # "Я из Родителя"
print(объект._Ребёнок__мой_секрет)  # "А я из Ребёнка!"

Короче, суть в чём, блядь:
_ — это как табличка «Не влезай, убьёт!». Залезешь — сам дурак.
__ — это уже настоящая колючая проволока под напряжением. Питон сам меняет имя, чтобы в наследниках не было пиздеца.