На этом шаге мы рассмотрим вопросы, связанные с использованием одного и двух подчеркиваний.
Вам нужно инкапсулировать "приватные" данные в экземпляре класса, но вас беспокоит, что в Python нет контроля доступа.
Вместо того чтобы полагаться на возможности языка по инкапсулированию данных, от программистов на Python ожидается соблюдение определенных соглашений о наименовании, касающихся намеренного использования данных и методов. Первое соглашение состоит в том, что любое имя, которое начинается с одного нижнего подчеркивания (_), должно рассматриваться как внутренняя реализация. Например:
class A: def _init__(self): self._internal = 0 # Внутренний атрибут self.public = 1 # Внешний атрибут def public_method(self): '''' A public method '''' . . . def _internal_method(self): . . .
Python не запрещает доступ к внутренним именам. Однако это считается неправильным, и в результате может получиться хрупкий код. Также стоит отметить, что имена, начинающиеся с нижнего подчеркивания, еще используются для модулей и функций уровня модуля. Например, если вы видите имя модуля, которое начинается с нижнего подчеркивания (например, _socket), то это внутренняя реализация. Аналогично функции уровня модуля, такие как sys._getframe(), должны применяться очень осторожно.
Вы можете натолкнуться на имена внутри классов, которые начинаются с двух нижних подчеркиваний (__). Например:
class B: def __init__(self): self.__private = 0 def __private_method(self): . . . def public_method(self): . . . self.__private_method() . . .
Использование двойного нижнего подчеркивания вызывает искажение имени в другое. Если говорить конкретно, то приватные атрибуты в представленном выше классе переименуются в _B__private и _B__private method соответственно. Здесь вы можете спросить, зачем нужны такие искажения. Причина - наследование: такие атрибуты не могут быть переопределены через наследование. Например:
class C(B): def __init__(self): super().__init__() self.private = 1 # Не перегружает B.__private # Не перегружает B.__private_method() def __private_method(self): . . .
Здесь приватные имена __private и __private_method переименуются в _C__private и _C__private_method, которые отличаются от искаженных имен в базовом классе B.
Факт того, что существует два разных соглашения (одиночное нижнее подчеркивание против двойного) для определения "приватных" атрибутов, приводит к логичному вопросу того, какой стиль вам стоит использовать. Для большей части вашего кода вы, вероятно, должны делать ваши имена непубличными с помощью одного нижнего подчеркивания. Если же, однако, вы знаете, что ваш код будет использовать подклассы и имеет внутренние атрибуты, которые должны быть скрыты от подклассов, то используйте двойные подчеркивания.
Также стоит отметить, что иногда вы можете определить переменную, которая вступает в конфликт с зарезервированным именем. Для таких случаев нужно использовать нижнее подчеркивание в конце:
lambda_ = 2.0
# Завершающий _ помогает избежать проблем с ключевым словом lambda
Причина не использовать нижнее подчеркивание в начале имени в этом случае заключается в том, что это позволяет избежать сомнений по поводу причины его использования (то есть использование подчеркивания в начале может быть истолковано как указание на приватность значения). Использование одного подчеркивания в конце решает эту проблему.
На следующем шаге мы рассмотрим создание управляемых атрибутов.