Что означают одинарные и двойные подчеркивания перед именем объекта?
Что означают одинарные и двойные начальные подчеркивания перед именем объекта в Python?
Переведено автоматически
Ответ 1
Одинарное подчеркивание
В классах имена с начальным подчеркиванием указывают другим программистам, что атрибут или метод предназначен для использования внутри этого класса. Однако конфиденциальность никоим образом не обеспечивается. Использование передних символов подчеркивания для функций в модуле указывает на то, что его не следует импортировать откуда-либо еще.
Из руководства по стилю PEP-8:
_single_leading_underscore
: слабый индикатор "внутреннего использования". Например.from M import *
не импортирует объекты, имя которых начинается с подчеркивания.
Двойное подчеркивание (искажение имени)
Любой идентификатор вида
__spam
(по крайней мере, два начальных подчеркивания, не более одного завершающего подчеркивания) текстуально заменяется на_classname__spam
, гдеclassname
- текущее имя класса с удаленными начальными подчеркиваниями. Это искажение выполняется без учета синтаксической позиции идентификатора, поэтому его можно использовать для определения частного экземпляра класса и переменных класса, методов, переменных, хранящихся в глобальных файлах, и даже переменных, хранящихся в экземплярах. закрыто для этого класса в экземплярах других классов.
И предупреждение с той же страницы:
Искажение имен предназначено для того, чтобы предоставить классам простой способ определять “частные” переменные и методы экземпляра, не беспокоясь о переменных экземпляра, определенных производными классами, или манипулируя переменными экземпляра кодом вне класса. Обратите внимание, что правила искажения разработаны в основном для предотвращения несчастных случаев; для определенного пользователя все еще возможно получить доступ к переменной, которая считается частной, или изменить ее.
Пример
>>> class MyClass():
... def __init__(self):
... self.__superprivate = "Hello"
... self._semiprivate = ", world!"
...
>>> mc = MyClass()
>>> print mc.__superprivate
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: myClass instance has no attribute '__superprivate'
>>> print mc._semiprivate
, world!
>>> print mc.__dict__
{'_MyClass__superprivate': 'Hello', '_semiprivate': ', world!'}
Ответ 2
_foo
: Всего лишь соглашение. Способ для программиста указать, что переменная является частной (что бы это ни означало в Python).__foo
: Это имеет реальное значение. Интерпретатор заменяет это имя на_classname__foo
, чтобы гарантировать, что имя не будет перекрываться с аналогичным именем в другом классе.__foo__
: Всего лишь соглашение. Способ использования системой Python имен, которые не будут конфликтовать с именами пользователей.
Никакие другие формы подчеркивания не имеют значения в мире Python. Кроме того, в этих соглашениях нет разницы между классом, переменной, глобальным и т.д.
Ответ 3
Пока отличные ответы, но не хватает некоторых лакомых кусочков. Одинарное подчеркивание в начале - это не совсем просто соглашение: если вы используете from foobar import *
, а модуль foobar
не определяет __all__
список, имена, импортированные из модуля, не включают имена с начальным подчеркиванием. Допустим, это в основном условно, поскольку в данном случае это довольно неясный угол;-).
Соглашение о подчеркивании перед началом широко используется не только для частных имен, но и для того, что C ++ назвал бы защищенными - например, имена методов, которые полностью предназначены для переопределения подклассами (даже те, которые должны быть переопределены, поскольку в базовом классе они raise NotImplementedError
!-) часто являются именами, начинающимися с одинарного подчеркивания, чтобы указать коду, использующему экземпляры этого класса (или подклассов), что указанные методы не предназначены для прямого вызова.
Например, чтобы создать потокобезопасную очередь с другой дисциплиной обслуживания, отличной от FIFO, нужно импортировать Queue, subclasses Queue .Очередь и переопределяет такие методы, как _get
и _put
; "клиентский код" никогда не вызывает эти методы ("hook"), а скорее ("организующие") общедоступные методы, такие как put
и get
(это известно как шаблон проектирования шаблонного метода - смотрите, например, Здесь интересную презентацию, основанную на видеозаписи моего выступления на эту тему, с добавлением кратких описаний стенограммы).
Редактировать: Ссылки на видео в описании выступлений теперь разорваны. Вы можете найти первые два видео здесь и здесь.
Ответ 4
._variable
является полуприватным и предназначен только для условностей
.__variable
часто ошибочно считается сверхпринадлежным, в то время как на самом деле это означает просто использовать namemangle для предотвращения случайного доступа[1]
.__variable__
обычно зарезервировано для встроенных методов или переменных
Вы все равно можете получить доступ к .__mangled
переменным, если вам этого отчаянно хочется. Двойное подчеркивание просто изменяет название или переименовывает переменную во что-то вроде instance._className__mangled
Пример:
class Test(object):
def __init__(self):
self.__a = 'a'
self._b = 'b'
>>> t = Test()
>>> t._b
'b'
t._b доступен, потому что он скрыт только по соглашению
>>> t.__a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__a'
t.__a не найден, потому что он больше не существует из-за изменения имени
>>> t._Test__a
'a'
Используя instance._className__variable
вместо просто имени с двойным подчеркиванием, вы можете получить доступ к скрытому значению