Что означает одно-и двойное подчеркивание перед именем объекта?
Я хочу прояснить это раз и навсегда. Может ли кто-нибудь объяснить точное значение наличия ведущих подчеркиваний перед именем объекта в Python? Также объясните разницу между одиночным и двойным ведущим подчеркиванием. Кроме того, остается ли это значение неизменным, является ли рассматриваемый объект переменной, функцией, методом и т. д.?
13 ответов
Одного Символа Подчеркивания
имена в классе с ведущим подчеркиванием просто указывают другим программистам, что атрибут или метод предназначен для частного использования. Однако с самим именем ничего особенного не делается.
цитата PEP-8:
_single_leading_underscore: слабый индикатор" внутреннего использования". Е. Г.
from M import *
не импортирует объекты, имя которых начинается с подчеркивания.
двойная Подчеркивание (Имя Искажение)
любой идентификатор формы
__spam
(не менее двух ведущих подчеркиваний, не более одного завершающего подчеркивания) текстуально заменяется на_classname__spam
, гдеclassname
- это текущее имя класса с начальным подчеркиванием(подчеркиванием). Это манипулирование выполняется без учета синтаксической позиции идентификатора, поэтому его можно использовать для определения класса-частного экземпляра и переменных класса, методов, переменные, хранящиеся в глобалах, и даже переменные, хранящиеся в экземплярах. private для этого класса на экземплярах других классов.
и предупреждение с той же страницы:
name mangling предназначен для того, чтобы дать классам простой способ определить "частные" переменные и методы экземпляра, не беспокоясь о переменных экземпляра, определенных производными классами, или mucking с переменными экземпляра кодом вне класса. Обратите внимание, что правила mangling разработанный в основном, чтобы избежать несчастных случаев; все еще возможно для решительной души получить доступ или изменить переменную, которая считается частной.
пример
>>> 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!'}
отличные ответы до сих пор, но некоторые лакомые кусочки отсутствуют. Одно ведущее подчеркивание-это не совсем просто соглашение: если вы используете from foobar import *
, модуль foobar
не определяет __all__
список, имена, импортированные из модуля не включить те, с ведущим подчеркиванием. Допустим это в основном соглашение, так как этот случай довольно неясный угол;-).
конвенция лидирующего подчеркивания широко используется не только для частная имена, но и для того, что C++ назвал бы защищенный ones -- например, имена методов, которые полностью предназначены для переопределения подклассами (даже те, которые есть для переопределения, так как в базовом классе они raise NotImplementedError
!- ) часто имена с одним ведущим подчеркиванием указывают на код используя экземпляры этого класса (или подклассов), которые упомянутые методы не предназначены для прямого вызова.
например, создайте потокобезопасную очередь с другой дисциплиной очереди, чем FIFO, одна очередь импорта, очередь подклассов.Queue, и переопределяет такие методы, как _get
и _put
; "клиентский код" никогда не вызывает эти ("крючковые") методы, а скорее ("организующие") публичные методы, такие как put
и get
(это известно как Метод Шаблона шаблон -- см., например,здесь для интересной презентации, основанной на видео моего разговора на эту тему, с добавление конспектов стенограммы).
__foo__
: Это просто соглашение, способ для системы Python использовать имена, которые не будут конфликтовать с именами пользователей.
_foo
: это просто соглашение, способ для программиста указать, что переменная является частной (что бы это ни значило в Python).
__foo
: это имеет реальное значение: интерпретатор заменяет это имя на _classname__foo
как способ гарантировать, что имя не будет перекрываться с аналогичным именем в другом классе.
никакой другой формы подчеркивает смысл в Python в мире.
в этих соглашениях нет разницы между class, variable, global и т. д.
._variable
- это койкомест и предназначены только для
.__variable
часто неправильно считается superprivate, в то время как это фактическое значение просто namemangle к предотвратить случайный доступ[1]
.__variable__
обычно зарезервировано для встроенных методов или переменных
вы все еще можете получить доступ .__mangled
переменные, если вы отчаянно хотите. Двойной символ подчеркивает только namemangles или переименовывает переменную в что-то вроде instance._className__mangled
пример:
class Test(object):
def __init__(self):
self.__a = 'a'
self._b = 'b'
>>> t = Test()
>>> t._b
'b'
Т._b доступен, потому что он будет скрыт только по
>>> t.__a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Test' object has no attribute '__a'
т.__a не найден, потому что он больше не существует из-за namemangling
>>> t._Test__a
'a'
по ссылке instance._className__variable
вместо имени с двойным подчеркиванием вы можете получить доступ к скрытому значению
одиночное подчеркивание в начале:
Python не имеет реальных частных методов. Вместо этого одно подчеркивание в начале имени метода или атрибута означает, что вы не должны обращаться к этому методу, потому что он не является частью API.
class BaseForm(StrAndUnicode):
def _get_errors(self):
"Returns an ErrorDict for the data provided for the form"
if self._errors is None:
self.full_clean()
return self._errors
errors = property(_get_errors)
(этот фрагмент кода был взят из исходного кода django: django/forms/forms.py). В этом коде errors
является общедоступным свойством, но метод, вызываемый этим свойством, _get_errors, является "частным", поэтому вы не должны доступ к нему.
два подчеркивания в начале:
это вызывает много путаницы. Он не должен использоваться для создания частного метода. Он должен использоваться, чтобы избежать переопределения вашего метода подклассом или случайного доступа. Давайте рассмотрим пример:
class A(object):
def __test(self):
print "I'm a test method in class A"
def test(self):
self.__test()
a = A()
a.test()
# a.__test() # This fails with an AttributeError
a._A__test() # Works! We can access the mangled name directly!
выход:
$ python test.py
I'm test method in class A
I'm test method in class A
Теперь создайте подкласс B и сделайте настройку для _ _ test method
class B(A):
def __test(self):
print "I'm test method in class B"
b = B()
b.test()
выход быть....
$ python test.py
I'm test method in class A
как мы видели, A. test () не вызывал методы B.__test (), как мы могли бы ожидать. Но на самом деле это правильное поведение для __. Два метода, называемые __test (), автоматически переименовываются (искажаются) в _A__test () и _B__test (), поэтому они случайно не переопределяются. Когда вы создаете метод, начинающийся с__, это означает, что вы не хотите, чтобы кто-либо мог переопределить его, и вы только собираетесь получить доступ к нему изнутри его собственного класс.
два подчеркивания в начале и в конце:
когда мы видим такой метод, как __this__
, не называй это. Это метод, который python должен вызывать, а не вы. Давайте посмотрим:
>>> name = "test string"
>>> name.__len__()
11
>>> len(name)
11
>>> number = 10
>>> number.__add__(40)
50
>>> number + 50
60
всегда есть оператор или собственная функция, которая вызывает эти магические методы. Иногда это просто крючок, который python вызывает в определенных ситуациях. Например __init__()
вызывается, когда объект создается после __new__()
называется создайте экземпляр...
возьмем пример...
class FalseCalculator(object):
def __init__(self, number):
self.number = number
def __add__(self, number):
return self.number - number
def __sub__(self, number):
return self.number + number
number = FalseCalculator(20)
print number + 10 # 10
print number - 20 # 40
дополнительные сведения см. В разделе руководство PEP-8. Дополнительные магические методы см. В разделе этот PDF.
иногда у вас есть то, что кажется кортежем с ведущим подчеркиванием, как в
def foo(bar):
return _('my_' + bar)
в этом случае происходит то, что _ () является псевдонимом для функции локализации, которая работает с текстом, чтобы поместить его на правильный язык и т. д. на основе локали. Например, Сфинкс делает это, и вы найдете среди импорта
from sphinx.locale import l_, _
и в сфинкс.locale, _ () присваивается как псевдоним некоторой функции локализации.
подчеркивание ( _ ) в Python
ниже приведены различные места, где _ используется в Python:
Один Подчеркнуть:
- Переводчик
- название
- перед названием
двойная Подчеркивание:
__ведущий_двойной_подчеркивания
before_after
Одного Символа Подчеркивания
Переводчик:
_ возвращает значение последнего выполненного значения выражения в Python REPL
>>> a = 10
>>> b = 10
>>> _
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name '_' is not defined
>>> a+b
20
>>> _
20
>>> _ * 2
40
>>> _
40
>>> _ / 2
20
для игнорирования значений:
несколько раз мы не хотите, чтобы возвращаемые значения в то время назначили эти значения wnderscore. Он использовался как одноразовая переменная.
# Ignore a value of specific location/index
for _ in rang(10)
print "Test"
# Ignore a value when unpacking
a,b,_,_ = my_method(var1)
название
Python имеет свои ключевые слова по умолчанию, которые мы не можем использовать в качестве имени переменной. Чтобы избежать такого конфликта между ключевым словом python и переменной, мы используем подчеркивание после имени
пример:
>>> class MyClass():
... def __init__(self):
... print "OWK"
>>> def my_defination(var1 = 1, class_ = MyClass):
... print var1
... print class_
>>> my_defination()
1
__main__.MyClass
>>>
перед названием
подчеркивания перед переменная / функция / имя метода указывает программисту, что он предназначен только для внутреннего использования, который может быть изменен, когда класс хочет.
здесь префикс подчеркивания считается непубличным. Если указать из импорта * , все имя начинается с _ не будет импортироваться.
Python не указывает действительно частные, поэтому эти могут быть вызваны непосредственно из других модулей, если он указан в все, мы также называем его слабым частная
class Prefix:
... def __init__(self):
... self.public = 10
... self._private = 12
>>> test = Prefix()
>>> test.public
10
>>> test._private
12
Python class_file.py
def public_api():
print "public api"
def _private_api():
print "private api"
вызов файл из REPL
>>> from class_file import *
>>> public_api()
public api
>>> _private_api()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name '_private_api' is not defined
>>> import class_file
>>> class_file.public_api()
public api
>>> class_file._private_api()
private api
Double Underscore(__)
__ведущий_двойной_подчеркивания
ведущее двойное подчеркивание говорит интерпретатору python переписать имя, чтобы избежать конфликта в подклассе.Интерпретатор изменяет имя переменной с расширением класса и этой функцией, известной как искажение. testFile.py
class Myclass():
def __init__(self):
self.__variable = 10
вызов из REPL
>>> import testFile
>>> obj = testFile.Myclass()
>>> obj.__variable
Traceback (most recent call last):
File "", line 1, in
AttributeError: Myclass instance has no attribute '__variable'
nce has no attribute 'Myclass'
>>> obj._Myclass__variable
10
в интерпретаторе python изменение имени переменной с помощью___. Так что несколько раз он использует как частный член, потому что другой класс не может получить доступ к этой переменной напрямую. Основная цель _ _ - использовать переменную/метод в классе, только если вы хотите использовать его вне класса, вы можете сделать открытый api
class Myclass():
def __init__(self):
self.__variable = 10
def func(self)
print self.__variable
вызов из REPL
>>> import testFile
>>> obj = testFile.Myclass()
>>> obj.func()
10
__ДО_ПОСЛЕ__
имя с началом С __ и заканчивается тем же, что и специальные методы в Python. Python предоставляет эти методы для использования в качестве перегрузки оператора в зависимости от пользователь.
Python предоставляет это Соглашение для различения пользовательской функции с функцией модуля
class Myclass():
def __add__(self,a,b):
print a*b
вызов из REPL
>>> import testFile
>>> obj = testFile.Myclass()
>>> obj.__add__(1,2)
2
>>> obj.__add__(5,2)
10
Если кто-то действительно хочет сделать переменную только для чтения, IMHO лучшим способом было бы использовать property() только с геттером, переданным ему. С имущества() мы можем иметь полный контроль над данными.
class PrivateVarC(object):
def get_x(self):
pass
def set_x(self, val):
pass
rwvar = property(get_p, set_p)
ronly = property(get_p)
Я понимаю, что OP задал немного другой вопрос, но поскольку я нашел другой вопрос, задающий "как установить частные переменные", отмеченный дубликатом с этим, я подумал о добавлении этой дополнительной информации здесь.
одиночные ведущие подчеркивания-это соглашение. с точки зрения интерпретатора нет никакой разницы, начинаются ли имена с одного подчеркивания или нет.
двойные начальные и конечные подчеркивания используются для встроенных методов, таких как __init__
, __bool__
, etc.
двойные ведущие подчеркивания без конечных аналогов также являются соглашением, однако методы класса будут исковеркали интерпретатором. Для переменных или основной функции имен не существует.
ваш вопрос хорош, речь идет не только о методах. Функции и объекты в модулях обычно имеют префикс с одним подчеркиванием и могут иметь префикс два.
но_ _ double _ underscore имена не искажаются в модулях, например. Что происходит, так это то, что имена, начинающиеся с одного (или нескольких) подчеркиваний, не импортируются, если вы импортируете все из модуля (из импорта модуля *), а также имена, показанные в справке(модуле).
вот простой иллюстративный пример того, как свойства двойного подчеркивания могут влиять на унаследованный класс. Итак, со следующей настройкой:
class parent(object):
__default = "parent"
def __init__(self, name=None):
self.default = name or self.__default
@property
def default(self):
return self.__default
@default.setter
def default(self, value):
self.__default = value
class child(parent):
__default = "child"
Если вы затем создадите дочерний экземпляр в Python REPL, вы увидите ниже
child_a = child()
child_a.default # 'parent'
child_a._child__default # 'child'
child_a._parent__default # 'parent'
child_b = child("orphan")
## this will show
child_b.default # 'orphan'
child_a._child__default # 'child'
child_a._parent__default # 'orphan'
это может быть очевидно для некоторых, но это застало меня врасплох в гораздо более сложной среде
"частные" переменные экземпляра, к которым нельзя получить доступ, кроме как изнутри объекта, не существуют в Python. Однако существует соглашение, за которым следует большинство кода Python: имя с префиксом подчеркивания (например, _spam) должно рассматриваться как непубличная часть API (будь то функция, метод или элемент данных). Он должен рассматриваться как деталь реализации и может быть изменен без уведомление.
ссылка https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references
получить факты _ и _ _ довольно легко; другие ответы выражают их довольно хорошо. Использование гораздо труднее определить.
вот как я это вижу:
_
следует использовать, чтобы указать, что функция не предназначена для публичного использования, например API. Это и ограничение импорта делают его поведение очень похожим на internal
в C#.
__
следует использовать, чтобы избежать столкновения имен в наследовании hirarchy и избежать latebinding. Очень похожий частная в C#.
==>
если вы хотите указать, что что-то не для общественного использования, но оно должно действовать как protected
использовать _
.
Если вы хотите указать, что что-то не для общественного использования, но оно должно действовать как private
использовать __
.
это тоже цитата, которая мне очень нравится:
проблема в том, что автор класса вправе думать "это имя атрибута / метода должно быть закрытым, доступным только из в течение это определение класса" и использовать соглашение _ _ private. Но позже, пользователь этого класса может сделать подкласс, который законно должен доступ к этому имени. Поэтому либо суперкласс должен быть изменен (что может быть сложно или невозможно), или код подкласса должен используйте вручную искаженные имена (которые в лучшем случае уродливы и хрупки).
но проблема в том, что, на мой взгляд, если нет IDE, которая предупреждает вас, когда вы переопределяете методы, находя ошибка может занять некоторое время, если вы случайно переопределили метод из базового класса.