Что такое одинарное и двойное подчеркивание перед именем объекта?

Вы когда-нибудь видели символы подчеркивания в имени объекта? Задумывались ли вы, что означают эти символы подчеркивания и зачем они нам нужны? В этой статье мы углубимся в объектно-ориентированное программирование на Python и попытаемся понять значение одинарного и двойного подчеркивания перед именем объекта.
Что такое ООП (объектно-ориентированное программирование)?
Объектно-ориентированное программирование — это способ написания кода. Наша цель — создать классы, состоящие из атрибутов, известных как данные-члены, и функций для управления этими атрибутами. ООП обладает такими свойствами, как полиморфизм, наследование, инкапсуляция и абстракция.
Связанный: узнайте больше об объектно-ориентированном программировании на Python.
Давайте обсудим все эти свойства объектно-ориентированного программирования, чтобы лучше понять его.
Инкапсуляция
Инкапсуляция достигается в ООП путем связывания данных-членов и функций-членов вместе в классе, хранящемся в одном блоке памяти. Функции-члены могут получать доступ к данным-членам класса, скрывая данные-члены от функций, не являющихся членами класса.
Связанный: Узнайте больше об инкапсуляции в Python.
Полиморфизм
Полиморфизм — это один интерфейс и множество методов. Это относится к способности объектно-ориентированного программирования предоставлять несколько функций для одного интерфейса. Он продвигает принцип DRY (не повторяйтесь) объектно-ориентированного программирования. Полиморфизм может быть достигнут перегрузкой операторов или перегрузкой функций. Мы должны быть осторожны при перегрузке операторов или функций, так как это может привести к двусмысленности.
Наследование
Наследование — это процесс приобретения свойств и функций ранее созданного класса путем наследования класса. Класс, который наследует другой класс, называется дочерним классом, а класс, который был унаследован, называется родительским или базовым классом. Мы можем наследовать несколько классов в одном классе, приобретая свойства и функции всех унаследованных классов. Наследование также продвигает DRY-принцип ООП. Наследование также может привести к неоднозначности. Подробнее о неоднозначности из-за наследования мы поговорим позже в этой статье.
Связанный: Узнать больше о Наследование в Python.
Абстракция
Абстракция в ООП — это возможность скрывать свойства или ограничивать доступ к элементам данных или функциям-членам класса. Это может быть достигнуто с помощью спецификаторов доступа, таких как private и public. Спецификатор доступа public позволяет всей программе получить доступ к члену, а private позволяет только функциям-членам класса обращаться к члену, для которого используется спецификатор доступа.
Как добиться абстракции в Python?
Если вы использовали C++ или Java, у них есть такие спецификаторы доступа, как public и private, которые можно использовать в ООП для достижения абстракции. Python не имеет спецификаторов доступа. Причина этого в том, что Python стремится иметь как можно более упрощенный синтаксис, чтобы программисты могли сосредоточиться на других вещах, а не тратить время на написание больших синтаксисов, которые могут привести к путанице и увеличению количества ошибок. Изобретатель Python Гвидо Ван Россум однажды сказал (цитирую): «Изобилие синтаксиса приносит больше хлопот, чем помощи».
Спецификатор доступа увеличивает сложность кода и не способствует эффективному использованию ООП. Таким образом, все данные-члены и функции-члены по умолчанию становятся общедоступными в Python. Теперь вы, должно быть, думаете: «Подождите, значит ли это, что мы не можем реализовать абстракцию в Python? Это просто неуважение к ООП. Какой смысл быть языком ООП, не способным выполнять абстракции?»
Не волнуйся. Python вас здесь не разочарует. Python имеет другой способ достижения абстракции. Python использует знак подчеркивания перед именем членов для достижения абстракции. Когда вы объявляете член, двойное подчеркивание перед именем члена сделает его недоступным из класса. Это может показаться сложным, но оставайтесь с нами; вы это легко поймете.
Использование ведущего одинарного и двойного подчеркивания для приватизации члена
Ведущее одиночное подчеркивание
class sample_class:
def accessible(self):
print("You can access me out of the class' scope.")
def _inaccessible(self):
print("Don't access me out of the class' scope.")
В приведенном выше блоке кода мы использовали подчеркивание перед недоступной функцией. Причина этого не в какой-либо функциональности Python. Это просто номенклатура, используемая пользователями Python для указания закрытых членов класса.
Ведущее двойное подчеркивание
class sample_class:
def accessible(self):
print("You can access me out of the class' scope.")
def __inaccessible(self):
print("You cannot access me out of the class' scope :(")
В приведенном выше блоке кода мы сначала объявили класс с именем «sample_class». Затем мы объявили в нем две функции.
Первый доступен, и когда мы вызовем функцию, она напечатает: «Вы можете получить доступ ко мне вне области действия класса». Эта функция доступна за пределами класса, потому что она объявлена нормально, а все члены класса по умолчанию общедоступны в Python.
Вторая функция __inaccessible, и когда мы ее вызовем, она напечатает: «Вы не можете получить доступ ко мне вне области видимости класса». Эта функция недоступна вне области видимости класса, потому что мы начали имя функции со знака подчеркивания.
Теперь давайте проверим, работает приватизация функции или нет.
Во-первых, мы создадим объект образца _class.
Теперь попробуем получить доступ к доступной функции obj.

Большой! Когда мы пытаемся получить доступ к доступной функции из области класса, он печатает: «Вы можете получить доступ ко мне из области видимости класса». Теперь попробуем получить доступ к функции __inaccessible вне класса.

Выдает ошибку атрибута. Это значит, что мы это сделали. Мы не можем получить доступ к функции __inaccessible вне класса.
Использование ведущего одиночного подчеркивания для доступа к частному члену класса
Когда вы правильно проверяете приведенный выше вывод, он говорит: «AttributeError: объект ‘sample_class’ не имеет атрибута ‘__inaccessible’». Почему?
Этот метод не является надежным. Что делает Python, так это то, что вместо приватизации функции он меняет ее имя на _
Итак, в нашем случае имя функции __inaccessible было изменено на _sample_class__inaccessible. Теперь давайте попробуем получить к нему доступ с этим именем.
obj._sample_class__inaccessible()

Поэтому, когда мы пытаемся получить доступ к функции __inaccessible с помощью _sample_class__inaccessible(), мы получаем вывод «Вы не можете получить доступ ко мне вне области действия класса :(“. Это означает, что мы можем получить к ней доступ по имени времени.
Итак, мы поняли, почему мы используем одиночное подчеркивание перед именем объекта. Теперь давайте попробуем понять, почему мы используем двойное подчеркивание перед именем объекта.
Использование ведущих двойных подчеркиваний для искажения имен
Изучая наследование, мы узнали, что иногда наследование может приводить к двусмысленности. Это может быть случай, когда мы объявляем родителя и члена базового класса с одним и тем же именем.
Попробуем разобраться с помощью примера.
class Parent:
def member(self):
print("I am a member of Parent class.")
class Child(Parent):
def member(self):
print("I am a member of Child class.")
В приведенном выше блоке кода мы создали класс с именем Parent и объявили функцию-член. Мы также объявили дочерний класс и объявили в нем функцию-член с таким же именем.
Создал экземпляр класса Child и назвал его child_obj. Теперь попробуем получить доступ к функции-члену из этого объекта дочернего класса.

Поэтому, когда мы пытаемся получить доступ к функции-члену из дочернего объекта с тем же именем, что и функция-член в унаследованном родительском классе, интерпретатор вызывает функцию-член дочернего класса.
Как нам получить доступ к функции-члену унаследованного родительского класса?
Мы можем сделать это, используя двойное подчеркивание перед объявлением члена в классе.
class Parent:
def __member(self):
print("I am a member of Parent class.")
class Child(Parent):
def __member(self):
print("I am a member of Child class.")
Как и в случае с приватизацией, объявление имени переменной с помощью __ или двойного подчеркивания перед тем, как ее имя изменит свое имя на _<имя класса>__<имя функции>. Благодаря этому мы можем получить доступ к функции-члену родительского класса, которая имеет то же имя, что и функция-член дочернего класса.
child_obj = Child()
child_obj._Parent__member()
child_obj._Child__member()

Завершающее одиночное подчеркивание
Одиночное подчеркивание в конце в Python — это традиционная номенклатура, используемая для предотвращения конфликтов имен в Python. Когда мы часто хотим назвать наши переменные или функции определенным словом, возникает конфликт имен, потому что уже есть встроенная функция с таким именем.
Например, вы не можете назвать список — список, потому что список — это имя встроенного объекта. В таких случаях мы можем назвать список как «list_». Это очень распространенная номенклатура, используемая в Python. Мы используем эту номенклатуру, чтобы назвать класс «class_» или объект как «object_». Это хорошая практика, поскольку мы не тратим время на обдумывание имени для нашей переменной.
Двойное подчеркивание в конце
В Python нет конкретных вариантов использования двойных подчеркиваний. Одни только двойные подчеркивания в конце не имеют смысла в Python. Но в паре с начальными подчеркиваниями у него много функциональности. Давайте обсудим их.
Другие варианты использования двойного подчеркивания
Двойное подчеркивание перед и после имени переменной символизирует магические методы. Магические методы также известны как методы Дандера. Они очень распространены и чрезвычайно полезны в Python. Нам не нужно определять магические методы, они автоматически определяются интерпретатором. Попробуем подробнее разобраться в магических методах.
Чтобы проверить методы dunder переменной, используйте функцию dir. Давайте проверим дандер-методы строки.

Таким образом, когда мы вызываем функцию dir для строки, мы получаем все функции строковой структуры данных. Все функции типа __<function_name>__
являются функциями magic или dunder. Их нельзя вызвать напрямую. Например, метод __add__ указывает, что произойдет, если вы используете оператор «+» в строке. __mul__ для умножения, __len__ для функции len и т. д.
Наиболее важным магическим методом является метод __init__. Это конструктор класса. Класс должен иметь метод __init__ для инициализации всех элементов данных класса.
При желании магические методы можно изменить. Например, если вы хотите, чтобы оператор «+» вел себя по-другому, вы можете изменить метод __add__ в соответствии с вашими потребностями. Магические методы очень важны. Убедитесь, что вы правильно их понимаете.
Связанный: узнайте больше о магических методах в Python.
Заключение
Итак, мы изучили два метода, в которых используется символ подчеркивания перед именем объекта. Первый предназначался для указания доступности члена, а второй — для вызова функции-члена родительского класса, которая имеет то же имя, что и функция-член дочернего класса. На первый взгляд это может показаться трудным для понимания, но когда вы копнете глубже, вы поймете, что это очень просто, но важно.
Рекомендации
Официальная документация Python.
Переполнение стека ответ на тот же вопрос.
Ссылка на источник