Что означают одиночные и двойные подчёркивания перед именем объекта в Python?
Одинарные и двойные подчеркивания в Python
Одинарные и двойные ведущие подчеркивания в Python служат соглашениями об именовании для указания предполагаемой видимости и использования переменных, методов и атрибутов. Одинарное ведущее подчеркивание (_) сигнализирует о том, что имя предназначено для внутреннего использования внутри класса или модуля, в то время как двойное ведущее подчеркивание (__) запускает искажение имен (name mangling), которое преобразует имя для предотвращения случайных конфликтов имен в сценариях наследования. Эти соглашения не являются настоящими модификаторами доступа, а скорее культурными сигналами для разработчиков о том, как следует обращаться к этим элементам.
Содержание
- Одинарное ведущее подчеркивание (_var)
- Двойное ведущее подчеркивание (__var)
- Механика искажения имен
- Подход Python к контролю доступа
- Практические примеры и случаи использования
- Когда использовать каждое соглашение
- Специальные случаи: Дандер-методы
Одинарное ведущее подчеркивание (_var)
Одинарное ведущее подчеркивание — это широко принятое соглашение в Python, которое указывает на то, что имя предназначено для внутреннего использования. Когда вы видите имя переменной или метода, начинающееся с одинарного подчеркивания, это сигнализирует другим разработчикам, что этот элемент не следует использовать вне класса или модуля, где он определен.
Основные характеристики:
-
Нет принудительного выполнения в Python: В отличие от некоторых других языков, Python на самом деле не предотвращает доступ к именам с одинарным ведущим подчеркиванием. Вы все еще можете получить доступ к
_internal_varизвне класса, но соглашение предупреждает вас не делать этого. -
Концепция “защищенного”: Это ближайший эквивалент Python модификатору доступа “protected”, найденному в таких языках, как Java или C++, хотя это лишь соглашение, а не языковое ограничение.
-
Распространенное использование: Часто используется для методов и атрибутов, которые являются частью внутренней реализации класса, но могут быть полезны для подклассов или для отладки.
class MyClass:
def __init__(self):
self._internal_var = 42 # Для внутреннего использования
def _internal_method(self):
return "Это внутренний метод"
def public_method(self):
return self._internal_method()
Согласно Python Engineer, одинарные подчеркивания в основном предназначены для коммуникации между разработчиками, а не для принудительного выполнения языком.
Двойное ведущее подчеркивание (__var)
Двойное ведущее подчеркивание запускает искажение имен (name mangling), которое является механизмом преобразования атрибута для предотвращения конфликтов в сценариях наследования. Это ближайший эквивалент Python “приватному” доступу, хотя он все еще не является по-настоящему приватным.
Что делает искажение имен:
- Преобразует
__varв_ClassName__var - Применяется только в контексте классов (не на уровне модуля)
- Влияет только на имена с ровно двумя ведущими подчеркиваниями и не более чем одним trailing подчеркиванием
Как объясняется на Real Python, “двойное ведущее подчеркивание запускает искажение имен для атрибутов и методов класса”. Документация Python подтверждает, что “правила искажения имен в основном предназначены для предотвращения случайностей; все еще возможно получить доступ или изменить переменную, которая считается приватной”.
class MyClass:
def __init__(self):
self.__private_var = 42
obj = MyClass()
print(obj.__private_var) # Ошибка! AttributeError
print(obj._MyClass__private_var) # Работает: 42
Механика искажения имен
Искажение имен следует конкретному алгоритму, определенному в спецификации языка Python:
Правило преобразования:
- Любой идентификатор формы
__var_name(не менее двух ведущих подчеркиваний и не более одного trailing подчеркивания) заменяется на_classname__var_name classname— это имя текущего класса с удаленными ведущими подчеркиваниями
Пример преобразования:
class Example:
def __init__(self):
self.__data = "секрет"
# До искажения: __data
# После искажения: _Example__data
Как объясняет Nsikak Imoh, “любой идентификатор формы __var_name — не менее двух ведущих подчеркиваний и не более одного trailing подчеркивания — заменяется на _classname__var_name.”
Важные ограничения:
- Искажение имен происходит только в определениях классов, а не на уровне модуля
- Оно не применяется к именам, которые начинаются и заканчиваются двойными подчеркиваниями (дандер-методы)
- Преобразование является чисто текстовым и происходит во время компиляции
- Вы все еще можете получить доступ к искаженным именам напрямую при необходимости
Подход Python к контролю доступа
В отличие от таких языков, как Java, C++ или C#, Python не имеет настоящих модификаторов доступа. Как отмечается в обсуждении на Reddit, “Python не поддерживает модификаторы доступа, и protected особенно не имеет никакого смысла в Python.”
Философия Python:
- “Мы все взрослые люди” — разработчикам доверяют следовать соглашениям
- Искажение имен в основном существует для предотвращения случайных конфликтов имен в наследовании
- Настоящая приватность не является основной целью двойных подчеркиваний
Сравнение с другими языками:
| Язык | Приватный | Защищенный | Публичный |
|---|---|---|---|
| Python | Соглашение (__var) | Соглашение (_var) | По умолчанию |
| Java | private |
protected |
По умолчанию |
| C++ | private: |
protected: |
По умолчанию |
Согласно PEP 8, “двойные ведущие подчеркивания следует использовать только для предотвращения конфликтов имен с атрибутами в классах, предназначенных для наследования.”
Практические примеры и случаи использования
Пример 1: Базовое искажение имен
class BankAccount:
def __init__(self, balance):
self.__balance = balance # Происходит искажение имен
def deposit(self, amount):
self.__balance += amount
def get_balance(self):
return self.__balance
account = BankAccount(1000)
# print(account.__balance) # AttributeError!
print(account._BankAccount__balance) # Работает: 1000
Пример 2: Наследование и конфликты имен
class Base:
def __init__(self):
self.__private = "Приватный Base"
class Derived(Base):
def __init__(self):
super().__init__()
self.__private = "Приватный Derived" # Конфликта нет!
d = Derived()
print(d._Base__private) # "Приватный Base"
print(d._Derived__private) # "Приватный Derived"
Как объясняется на GeeksforGeeks, искажение имен “в основном предназначено для предотвращения случайностей; все еще возможно получить доступ или изменить переменную, которая считается приватной.”
Пример 3: Когда искажение имен не применяется
class MyClass:
def __init__(self):
self.__dunder_method__() # Это не будет искажено
def __dunder_method__(self):
print("Это специальный метод")
obj = MyClass() # Выводит: "Это специальный метод"
Когда использовать каждое соглашение
Используйте одинарное подчеркивание (_var), когда:
- Вы хотите указать на внутреннее использование, но все еще разрешить доступ
- Вы создаете методы или атрибуты, которые могут быть полезны для подклассов
- Вы хотите сигнализировать, что что-то является деталью реализации
- Вы следуете соглашению “protected” из других языков
Используйте двойное подчеркивание (__var), когда:
- Вы хотите предотвратить случайные конфликты имен в наследовании
- Вы создаете атрибуты, которые не должны быть легко доступны
- Вы работаете с классами, предназначенными для наследования
- Вы хотите четко показать, что что-то является приватным для реализации
Как отмечено на dbader.org, “искажение имен не применяется, если имя начинается и заканчивается двойными подчеркиваниями” — эти зарезервированы для специальных методов.
Специальные случаи: Дандер-методы
Имена, которые начинаются и заканчиваются двойными подчеркиваниями (такие как __init__, __str__, __add__), являются дандер (double underscore) методами и обрабатываются особым образом:
Основные характеристики:
- Не искажаются: Эти имена остаются неизменными в процессе искажения имен
- Специальное значение: Они реализуют или переопределяют специальное поведение в Python
- Соглашение: Должны использоваться только для реализации или переопределения существующих специальных методов
class MyClass:
def __init__(self): # Не искажается!
self.value = 42
def __str__(self): # Не искажается!
return str(self.value)
def __private_method(self): # Это будет искажено!
return "приватный"
Как отмечено в исследованиях, “Имена с двойными ведущими и trailing подчеркиваниями зарезервированы для специального использования в Python” и “остаются нетронутыми интерпретатором Python.”
Заключение
Соглашения об именовании с подчеркиваниями в Python предоставляют способ указать предполагаемую видимость и использование элементов кода, даже though язык не принудительно контролирует доступ. Одинарные ведущие подчеркивания сигнализируют о внутреннем использовании, в то время как двойные ведущие подчеркивания запускают искажение имен для предотвращения конфликтов в наследовании.
Ключевые выводы:
- Одинарные подчеркивания (_var) — это соглашения для внутреннего/защищенного доступа
- Двойные подчеркивания (__var) запускают искажение имен в _ClassName__var
- Python не имеет настоящего приватного доступа — это соглашения, а не языковые функции
- Искажение имен помогает предотвращать случайные конфликты имен в наследовании
- Дандер-методы (method) являются специальными и не искажаются
Рекомендации:
- Используйте одинарные подчеркивания для деталей реализации, которые могут быть полезны для подклассов
- Используйте двойные подчеркивания для действительно внутренних деталей реализации
- Помните, что оба соглашения можно обойти при необходимости
- Следуйте философии Python “мы все взрослые люди” — четко документируйте свои намерения
Эти соглашения об именовании являются частью того, что делает Python гибким и удобным для разработчиков, доверяя программистам использовать свое лучшее суждение, при этом предоставляя полезные механизмы для предотвращения распространенных ошибок.
Источники
- Single and Double Underscores in Python Names – Real Python
- What is the meaning of single and double underscore before an object name? - Stack Overflow
- Private Methods - Single or Double Underscore - Reddit
- The Meaning of Underscores in Python – dbader.org
- Single and Double Underscores in Python explained - Medium
- What is the meaning of single and double leading underscore in Python - Python Engineer
- PEP 8 – Style Guide for Python Code
- What’s the Meaning of Single and Double Underscores In Python? - Reddit
- Python double underscore mangling - Stack Overflow
- Single underscore and Double underscore in Python OOP - Medium
- Name Mangling in Python | Tech Tutorials
- What is Name Mangling in Python and How Does it Work With Detailed Examples
- Name Mangling in Python with Example
- 9. Classes — Python 3.14.0 documentation
- Python – Private Members In Class and Name Mangling
- Name mangling in Python - GeeksforGeeks