Что означают одинарные и двойные подчеркивания перед именем объекта в Python?
Одиночное и двойное подчеркивание в Python
В Python одиночный и двойной подчеркивания перед именем объекта представляют разные уровни предполагаемой приватности и следуют определенным соглашениям об именовании. Одиночное подчеркивание (_variable) указывает на слабый маркер “внутреннего использования”, сигнализируя разработчикам, что объект следует рассматривать как приватный и не обращаться к нему напрямую извне класса, хотя это чисто соглашение без фактического принудительного исполнения. Двойное подчеркивание (__variable) запускает механизм искажения имен (name mangling), который автоматически переименовывает атрибут, включая в него имя класса (становясь _ClassName__variable), что делает сложнее случайное переопределение в дочерних классах, но при этом остается технически доступным.
Содержание
- Соглашение с одиночным подчеркиванием
- Двойное подчеркивание и искажение имен
- Практические примеры и использование
- Когда использовать каждое соглашение
- Распространенные заблуждения
- Рекомендации PEP 8
Соглашение с одиночным подчеркиванием
Одиночное ведущее подчеркивание (_) используется как слабый индикатор предполагаемого внутреннего использования объекта. Согласно Real Python, это соглашение сигнализирует о том, что переменная или метод предназначены для приватного использования и не должны напрямую вызываться извне класса.
Ключевые характеристики:
- Основано на соглашении, а не на принуждении: Python не предотвращает доступ к именам с префиксом подчеркивания
- Слабая приватность: Действует как подсказка для других разработчиков о предполагаемой области видимости объекта
- Похоже на protected в Java: В аналогиях с языками программирования одиночное подчеркивание часто сравнивают с модификатором protected в Java
- Использование на уровне модуля: При использовании на уровне модуля (не в классах) указывает, что переменная предназначена для внутреннего использования в этом модуле
Как объясняет Python Engineer, “Переменные, объявленные в классе с одиночным ведущим подчеркиванием, по соглашению следует рассматривать как приватные, это работает как слабый индикатор, указывающий только на внутреннее использование.”
Пример:
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) объясняет этот механизм: “Если ваш класс предназначен для наследования, и у вас есть атрибуты, которые вы не хотите, чтобы дочерние классы использовали, рассмотрите возможность именования их с двойными ведущими подчеркиваниями и без конечных подчеркиваний.”
Технические детали:
class MyClass:
def __init__(self):
self.__private_value = 100 # Будет искажено до _MyClass__private_value
def __private_method(self):
return "Это строго приватный метод"
При доступе к этим атрибутам извне класса вы обнаружите, что они были переименованы:
obj = MyClass()
print(obj._MyClass__private_value) # Это работает - показывает искаженное имя
# print(obj.__private_value) # Это вызовет AttributeError
Практические примеры и использование
Практическое использование одиночного подчеркивания:
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)
Двойное подчеркивание с искажением имен:
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}"
Реальный сценарий использования искажения имен:
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 не обеспечивает настоящей приватности (в отличие от некоторых других языков), эти шаблоны именования предоставляют четкие сигналы о предполагаемом использовании, сохраняя философию языка “мы все здесь взрослые и согласные”.
Источники
- Real Python - Single and Double Underscores in Python Names
- PEP 8 - Style Guide for Python Code
- Python Engineer - What is the meaning of single and double leading underscore in Python
- dbader.org - The Meaning of Underscores in Python
- Stack Overflow - What is the meaning of single and double underscore before an object name?
- Software Engineering Stack Exchange - What is the historical reason why Python uses the double underscore for Class Private members