НейроАгент

Руководство по символу подчеркивания в Python: Одинарное vs Двойное

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

Вопрос

Что означают одинарные и двойные подчеркивания перед именем объекта в Python?

НейроАгент

Одиночное и двойное подчеркивание в Python

В Python одиночный и двойной подчеркивания перед именем объекта представляют разные уровни предполагаемой приватности и следуют определенным соглашениям об именовании. Одиночное подчеркивание (_variable) указывает на слабый маркер “внутреннего использования”, сигнализируя разработчикам, что объект следует рассматривать как приватный и не обращаться к нему напрямую извне класса, хотя это чисто соглашение без фактического принудительного исполнения. Двойное подчеркивание (__variable) запускает механизм искажения имен (name mangling), который автоматически переименовывает атрибут, включая в него имя класса (становясь _ClassName__variable), что делает сложнее случайное переопределение в дочерних классах, но при этом остается технически доступным.

Содержание

Соглашение с одиночным подчеркиванием

Одиночное ведущее подчеркивание (_) используется как слабый индикатор предполагаемого внутреннего использования объекта. Согласно Real Python, это соглашение сигнализирует о том, что переменная или метод предназначены для приватного использования и не должны напрямую вызываться извне класса.

Ключевые характеристики:

  • Основано на соглашении, а не на принуждении: Python не предотвращает доступ к именам с префиксом подчеркивания
  • Слабая приватность: Действует как подсказка для других разработчиков о предполагаемой области видимости объекта
  • Похоже на protected в Java: В аналогиях с языками программирования одиночное подчеркивание часто сравнивают с модификатором protected в Java
  • Использование на уровне модуля: При использовании на уровне модуля (не в классах) указывает, что переменная предназначена для внутреннего использования в этом модуле

Как объясняет Python Engineer, “Переменные, объявленные в классе с одиночным ведущим подчеркиванием, по соглашению следует рассматривать как приватные, это работает как слабый индикатор, указывающий только на внутреннее использование.”

Пример:

python
class MyClass:
    def __init__(self):
        self._internal_value = 42  # Одиночное подчеркивание - внутреннее использование
    
    def _internal_method(self):
        return "Это внутренний метод"
    
    def public_method(self):
        return self._internal_method()

Двойное подчеркивание и искажение имен

Двойное ведущее подчеркивание (__) запускает механизм искажения имен (name mangling) Python, который представляет собой более надежную форму защиты приватности. Согласно Real Python, “Python автоматически выполняет искажение имен, когда вы добавляете префикс из двух подчеркиваний (__ к имени атрибута).”

Как работает искажение имен:

  • Правило трансформации: __attribute становится _ClassName__attribute
  • Включение имени класса: Текущее имя класса (без ведущих подчеркиваний) добавляется в начало
  • Автоматическое переименование: Интерпретатор Python текстово заменяет идентификатор во время компиляции
  • Ограниченная область применения: Применяется только к именам с ровно двумя ведущими подчеркиваниями и не более чем одним конечным подчеркиванием

Python Enhancement Proposal 8 (PEP 8) объясняет этот механизм: “Если ваш класс предназначен для наследования, и у вас есть атрибуты, которые вы не хотите, чтобы дочерние классы использовали, рассмотрите возможность именования их с двойными ведущими подчеркиваниями и без конечных подчеркиваний.”

Технические детали:

python
class MyClass:
    def __init__(self):
        self.__private_value = 100  # Будет искажено до _MyClass__private_value
    
    def __private_method(self):
        return "Это строго приватный метод"

При доступе к этим атрибутам извне класса вы обнаружите, что они были переименованы:

python
obj = MyClass()
print(obj._MyClass__private_value)  # Это работает - показывает искаженное имя
# print(obj.__private_value)  # Это вызовет AttributeError

Практические примеры и использование

Практическое использование одиночного подчеркивания:

python
class DatabaseConnection:
    def __init__(self, connection_string):
        self._connection = None  # Внутренний объект соединения
        self._is_connected = False
        self.connection_string = connection_string  # Публичный API
    
    def _connect(self):
        """Внутренний метод для установления соединения"""
        self._connection = establish_connection(self.connection_string)
        self._is_connected = True
    
    def execute_query(self, query):
        """Публичный метод, использующий внутренние методы"""
        if not self._is_connected:
            self._connect()
        return self._connection.execute(query)

Двойное подчеркивание с искажением имен:

python
class BaseClass:
    def __init__(self):
        self.__private_data = "Приватные данные базового класса"
    
    def __private_method(self):
        return "Приватный метод базового класса"

class SubClass(BaseClass):
    def __init__(self):
        super().__init__()
        self.__private_data = "Приватные данные дочернего класса"  # Не переопределит базовый класс
    
    def demonstrate_mangling(self):
        # Приватные данные базового класса (искаженные)
        base_data = self._BaseClass__private_data
        # Приватные данные дочернего класса (искаженные)
        sub_data = self._SubClass__private_data
        return f"Базовый: {base_data}, Дочерний: {sub_data}"

Реальный сценарий использования искажения имен:

python
class Widget:
    def __init__(self):
        self.__internal_state = {}
    
    def add_state(self, key, value):
        self.__internal_state[key] = value
    
    def get_state(self, key):
        return self.__internal_state.get(key, None)

# Сценарий множественного наследования
class ClickableWidget(Widget):
    def __init__(self):
        super().__init__()
        self.__internal_state = {}  # Отличается от __internal_state Widget
    
    def handle_click(self):
        # Это не будет мешать __internal_state Widget
        self.__internal_state['click_count'] = self.__internal_state.get('click_count', 0) + 1

Когда использовать каждое соглашение

Используйте одиночное подчеркивание, когда:

  • Вы хотите указать на внутреннее использование без накладных расходов на искажение имен
  • Вы работаете над небольшим кодом, где командные соглашения хорошо понятны
  • Вам нужен доступ к атрибуту в дочерних классах (искажение имен предотвращает это)
  • Вы хотите избежать незначительных накладных расходов на производительность от искажения имен

Используйте двойное подчеркивание, когда:

  • У вас есть класс, который, вероятно, будет наследоваться
  • Вы хотите предотвратить случайные конфликты имен в иерархиях наследования
  • Вам нужна более сильная защита от случайного переопределения
  • Вы работаете над библиотекой или фреймворком с множеством потенциальных пользователей

Согласно dbader.org, “Префикс двойного подчеркивания заставляет интерпретатор Python переименовывать атрибут, чтобы избежать конфликтов имен в дочерних классах.”

Распространенные заблуждения

Заблуждение: Двойные подчеркивания делают атрибуты действительно приватными

Многие разработчики считают, что двойные подчеркивания делают атрибуты полностью недоступными, но это неверно. Как объясняется на Stack Overflow, “.__variable часто ошибочно считается суперприватным, что несколько вводит в заблуждение. Двойные подчеркивания, хотя и выполняют искажение имен, делают это в попытке сделать его должным образом приватным.”

Заблуждение: Одиночное подчеркивание принудительно исполняется Python

Одиночное подчеркивание - это чисто соглашение. Как указано на Software Engineering Stack Exchange, “Одиночные подчеркивания используются не только для приватных элементов модуля, но и для приватных членов класса. Разница между одиночными и двойными подчеркиваниями заключается только в искажении имен.”

Заблуждение: Двойные подчеркивания обеспечивают безопасность

Ни одно из соглашений с подчеркиваниями не обеспечивает реальной безопасности или приватности. Как уточняется на Reddit, “Python не имеет такого механизма. Префикс двойного подчеркивания запускает искажение имен.”

Рекомендации PEP 8

Официальное руководство по стилю PEP 8 дает четкие указания по использованию подчеркиваний:

“Если ваш класс предназначен для наследования, и у вас есть атрибуты, которые вы не хотите, чтобы дочерние классы использовали, рассмотрите возможность именования их с двойными ведущими подчеркиваниями и без конечных подчеркиваний. Это вызывает алгоритм искажения имен Python, в котором имя класса включается в имя атрибута.”

PEP 8 также отмечает, что:

  • Только два соглашения об именовании принудительно исполняют определенные поведения Python: двойное ведущее подчеркивание запускает искажение имен
  • Одиночное подчеркивание - чисто соглашение, указывающее на непубличную видимость
  • Имена с двойными подчеркиваниями в начале и в конце (как __init__) зарезервированы для специальных методов

Блог Python Engineer резюмирует подход PEP 8: “Имена переменных с ведущим подчеркиванием следует рассматривать как приватные. Это просто соглашение, и Python не его принудительно исполняет.”


Заключение

Ключевые выводы:

  • Одиночное подчеркивание (_): Слабый индикатор приватности, сигнализирующий о внутреннем использовании, чисто соглашение без принудительного исполнения языком
  • Двойное подчеркивание (__): Запускает искажение имен, автоматически переименовывая атрибуты для предотвращения конфликтов в дочерних классах
  • Механизм искажения имен: Преобразует __attr в _ClassName__attr, делая случайные переопределения менее вероятными
  • Практическое различие: Одиночное подчеркивание позволяет доступ в дочерних классах, двойное подчеркивание обеспечивает более сильную защиту в сценариях наследования

Рекомендации по действию:

  • Используйте одиночное подчеркивание для внутренних методов и атрибутов, которым нужна некоторая защита, но которые могут быть доступны в дочерних классах
  • Используйте двойное подчеркивание для классов, которые будут наследоваться, чтобы предотвратить конфликты имен
  • Никогда не полагайтесь на соглашения с подчеркиваниями для реальной безопасности - это подсказки, а не барьеры
  • Четко документируйте ваш API независимо от использования подчеркиваний
  • Рассмотрите использование свойств (@property) для контролируемого доступа к внутреннему состоянию

Соглашения с подчеркиваниями в Python представляют собой продуманный баланс между гибкостью и безопасностью. Хотя Python не обеспечивает настоящей приватности (в отличие от некоторых других языков), эти шаблоны именования предоставляют четкие сигналы о предполагаемом использовании, сохраняя философию языка “мы все здесь взрослые и согласные”.

Источники

  1. Real Python - Single and Double Underscores in Python Names
  2. PEP 8 - Style Guide for Python Code
  3. Python Engineer - What is the meaning of single and double leading underscore in Python
  4. dbader.org - The Meaning of Underscores in Python
  5. Stack Overflow - What is the meaning of single and double underscore before an object name?
  6. Software Engineering Stack Exchange - What is the historical reason why Python uses the double underscore for Class Private members