НейроАгент

Подчеркивания в Python: соглашения об именовании

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

Вопрос

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

НейроАгент

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

Одинарные и двойные ведущие подчеркивания в Python служат соглашениями об именовании для указания предполагаемой видимости и использования переменных, методов и атрибутов. Одинарное ведущее подчеркивание (_) сигнализирует о том, что имя предназначено для внутреннего использования внутри класса или модуля, в то время как двойное ведущее подчеркивание (__) запускает искажение имен (name mangling), которое преобразует имя для предотвращения случайных конфликтов имен в сценариях наследования. Эти соглашения не являются настоящими модификаторами доступа, а скорее культурными сигналами для разработчиков о том, как следует обращаться к этим элементам.

Содержание

Одинарное ведущее подчеркивание (_var)

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

Основные характеристики:

  • Нет принудительного выполнения в Python: В отличие от некоторых других языков, Python на самом деле не предотвращает доступ к именам с одинарным ведущим подчеркиванием. Вы все еще можете получить доступ к _internal_var извне класса, но соглашение предупреждает вас не делать этого.

  • Концепция “защищенного”: Это ближайший эквивалент Python модификатору доступа “protected”, найденному в таких языках, как Java или C++, хотя это лишь соглашение, а не языковое ограничение.

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

python
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 подтверждает, что “правила искажения имен в основном предназначены для предотвращения случайностей; все еще возможно получить доступ или изменить переменную, которая считается приватной”.

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 — это имя текущего класса с удаленными ведущими подчеркиваниями

Пример преобразования:

python
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: Базовое искажение имен

python
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: Наследование и конфликты имен

python
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: Когда искажение имен не применяется

python
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
  • Соглашение: Должны использоваться только для реализации или переопределения существующих специальных методов
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 гибким и удобным для разработчиков, доверяя программистам использовать свое лучшее суждение, при этом предоставляя полезные механизмы для предотвращения распространенных ошибок.

Источники

  1. Single and Double Underscores in Python Names – Real Python
  2. What is the meaning of single and double underscore before an object name? - Stack Overflow
  3. Private Methods - Single or Double Underscore - Reddit
  4. The Meaning of Underscores in Python – dbader.org
  5. Single and Double Underscores in Python explained - Medium
  6. What is the meaning of single and double leading underscore in Python - Python Engineer
  7. PEP 8 – Style Guide for Python Code
  8. What’s the Meaning of Single and Double Underscores In Python? - Reddit
  9. Python double underscore mangling - Stack Overflow
  10. Single underscore and Double underscore in Python OOP - Medium
  11. Name Mangling in Python | Tech Tutorials
  12. What is Name Mangling in Python and How Does it Work With Detailed Examples
  13. Name Mangling in Python with Example
  14. 9. Classes — Python 3.14.0 documentation
  15. Python – Private Members In Class and Name Mangling
  16. Name mangling in Python - GeeksforGeeks