В языке Python не существуют закрытых переменных или методов экземпляра класса, к которым нельзя получить доступ, кроме как изнутри объекта, НО существует соглашение, которому следует большая часть кода Python.
Имя, которое определено в классе с префиксом подчеркивания, например _spam
, следует рассматривать как непубличную часть API, будь то метод или атрибут данных. Это соглашение следует считать деталями реализации и оно может быть изменено без предварительного уведомления.
Существует допустимый вариант использования закрытых членов класса. Чтобы избежать конфликта имен, определенных в классе с именами, определенными в подклассах, существует ограниченная поддержка такого механизма, как искажением имен. Любой идентификатор вида __spam
(не менее двух начальных подчеркиваний и не более одного конечного подчеркивания) текстуально заменяется на _classname__spam
, где classname
- это текущее имя класса с начальным подчеркиванием. Это искажение выполняется без учета синтаксической позиции идентификатора, если оно происходит в пределах определения класса.
Искажение имен полезно для того, чтобы позволить подклассам переопределять методы, не нарушая вызовов методов, расположенных внутри класса.
class Mapping: def __init__(self, iterable): self.items_list = [] self.__update(iterable) def update(self, iterable): for item in iterable: self.items_list.append(item) # закрытая копия оригинального метода update() __update = update class MappingSubclass(Mapping): def update(self, keys, values): # предоставляет новую сигнатуру для update() # но не ломается __init__() for item in zip(keys, values): self.items_list.append(item)
Приведенный выше пример будет работать, даже если в подкласс MappingSubclass
будет введен идентификатор __update
, так как он заменяется на _Mapping__update
в классе Mapping
и _MappingSubclass__update
в классе MappingSubclass
соответственно.
Обратите внимание, что правила искажения разработаны главным образом, чтобы избежать переназначений закрытых переменных. К закрытой переменной все еще возможно получить доступ или изменить ее. Это может быть даже полезно в особых случаях, таких как отладка кода.
Обратите внимание, что код, переданный в функции exec()
или eval()
, не рассматривает имя вызывающего класса - текущим классом. Это похоже на эффект оператора global
, эффект которого также ограничен кодом, который байтово скомпилирован вместе. То же ограничение применяется к getattr()
, setattr()
и delattr()
, а также при прямой ссылке на __dict__
.