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

Может ли кто-нибудь объяснить точный смысл наличия подчеркивания перед именем объекта в Python? Также объясните разницу между одинарным и двойным начальным подчеркиванием. Кроме того, остается ли это значение одинаковым, независимо от того, является ли рассматриваемый объект переменной, функцией, методом и т. Д.?

20 ответов

Решение

Единственный знак подчеркивания

Имена в классе с начальным подчеркиванием просто указывают другим программистам, что атрибут или метод предназначен для приватности. Однако ничего особенного с самим именем не делается.

Процитирую PEP-8:

_single_leading_underscore: слабый индикатор "внутреннего использования". Например from M import * не импортирует объекты, имя которых начинается с подчеркивания.

Двойное Подчеркивание (Название Mangling)

Из документов Python:

Любой идентификатор формы __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!'}

__foo__Это просто соглашение, способ для системы Python использовать имена, которые не будут конфликтовать с именами пользователей.

_fooЭто просто соглашение, способ для программиста указать, что переменная является закрытой (что бы это ни значило в Python).

__foo: это имеет реальное значение: переводчик заменяет это имя на _classname__foo как способ гарантировать, что имя не будет совпадать с аналогичным именем в другом классе.

Никакая другая форма подчеркивания не имеет значения в мире Python.

В этих соглашениях нет разницы между классом, переменной, глобальным и т. Д.

Отличные ответы до сих пор, но некоторые лакомые кусочки отсутствуют. Единственное подчеркивание - это не просто соглашение: если вы используете from foobar import * и модуль foobar не определяет __all__ список, имена, импортированные из модуля , не включают имена с начальным подчеркиванием. Допустим, это в основном соглашение, так как этот случай довольно неясен;-).

Соглашение о нижнем подчеркивании широко используется не только для частных имен, но и для того, что C++ назвал бы защищенными - например, имена методов, которые полностью предназначены для переопределения подклассами (даже те, которые должны быть переопределены, поскольку в базовый класс они raise NotImplementedError!-) часто являются именами с единственным начальным подчеркиванием, чтобы указать коду, использующему экземпляры этого класса (или подклассы), что указанные методы не предназначены для непосредственного вызова.

Например, чтобы создать потокобезопасную очередь с дисциплиной очередей, отличной от FIFO, необходимо импортировать очередь, подклассы Queue.Queue и переопределить такие методы, как _get а также _put; "код клиента" никогда не вызывает эти ("перехватывать") методы, а скорее ("организующие") публичные методы, такие как put а также get (это известно как шаблон проектирования " Шаблонный метод" - см., например, здесь интересную презентацию, основанную на видео моего выступления на эту тему, с добавлением кратких описаний стенограммы).

._variable является полуприватным и предназначен только для конвенции

.__variable часто ошибочно считается суперприватным, в то время как его реальный смысл - просто поменять имя, чтобы предотвратить случайный доступ [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 вместо только двойного имени подчеркивания вы можете получить доступ к скрытому значению

Одно подчеркивание в начале:

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.

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.

Согласно https://dbader.org/blog/meaning-of-underscores-in-python

  • Подчеркивание в начале строки (_var): соглашение об именах, указывающее, что имя предназначено для внутреннего использования. Обычно не применяется интерпретатором Python (за исключением импорта подстановочных знаков) и используется только как подсказка для программиста.
  • Одиночное подчеркивание в конце (var_): используется по соглашению, чтобы избежать конфликтов имен с ключевыми словами Python.
  • Двойное подчеркивание в начале (__var): вызывает искажение имени при использовании в контексте класса. Применяется интерпретатором Python.
  • Двойное начало и подчеркивание в конце (__var__): указывает на специальные методы, определенные языком Python. Избегайте этой схемы именования ваших собственных атрибутов.
  • Одиночный знак подчеркивания (_): Иногда используется как имя для временных или незначимых переменных ("безразлично"). Также: результат последнего выражения в Python REPL.

Поскольку так много людей имеют в виду речь Рэймонда, я просто сделаю это немного проще, записав то, что он сказал:

Намерение двойного подчеркивания не было о частной жизни. Намерение было использовать именно так, как это

class Circle(object):

    def __init__(self, radius):
        self.radius = radius

    def area(self):
        p = self.__perimeter()
        r = p / math.pi / 2.0
        return math.pi * r ** 2.0

    def perimeter(self):
        return 2.0 * math.pi * self.radius

    __perimeter = perimeter  # local reference


class Tire(Circle):

    def perimeter(self):
        return Circle.perimeter(self) * 1.25

Это на самом деле противоположность частной жизни, это все о свободе.Это делает ваши подклассы свободными для переопределения любого одного метода, не нарушая другие.

Скажем, вы не держите местную ссылку на perimeter в Circle, Теперь производный класс Tire отменяет реализацию perimeterбез прикосновения area, Когда вы звоните Tire(5).area()в теории это все еще следует использовать Circle.perimeter для вычисления, но на самом деле он использует Tire.perimeter, что не является предполагаемым поведением. Вот почему нам нужна местная ссылка в Circle.

Но почему __perimeter вместо _perimeter? Так как _perimeter все еще дает производному классу возможность переопределить:

class Tire(Circle):

    def perimeter(self):
        return Circle.perimeter(self) * 1.25

    _perimeter = perimeter

Двойное подчеркивание имеет искажение имени, поэтому существует очень малая вероятность того, что локальная ссылка в родительском классе переопределится в производном классе. таким образом "делает ваши подклассы свободными для переопределения любого одного метода, не нарушая другие".

Если ваш класс не будет унаследован, или переопределение метода ничего не нарушает, тогда вам просто не нужно __double_leading_underscore,

Иногда у вас есть то, что выглядит как кортеж с подчеркиванием, как в

def foo(bar):
    return _('my_' + bar)

В этом случае происходит то, что _() является псевдонимом для функции локализации, которая работает с текстом, переводя его на соответствующий язык и т. Д. В зависимости от локали. Например, Sphinx делает это, и вы найдете среди импортеров

from sphinx.locale import l_, _

а в sphinx.locale _() назначается псевдонимом некоторой функции локализации.

  • _var: переменные с ведущим одинарным подчеркиванием в python являются классическими переменными, предназначенными для информирования других пользователей вашего кода о том, что эта переменная должна быть зарезервирована для внутреннего использования. Они отличаются от классических переменных в одном: они не импортируются при импорте с подстановочными знаками объекта / модуля, в котором они определены (исключения при определении __all__переменная ). Например:

            # foo.py
    
    var = "var"
    _var = "_var"
    
            # bar.py
    
    from foo import *
    
    print(dir())  # list of defined objects, contains 'var' but not '_var'
    print(var)    # var
    print(_var)   # NameError: name '_var' is not defined
    
  • : одинарное подчеркивание - это частный случай ведущих одиночных переменных подчеркивания. По соглашению она используется как мусорная переменная для хранения значения, которое не предназначено для последующего доступа. Он также не импортируется при импорте подстановочных знаков. Например: это for цикл выводит «Я не должен разговаривать в классе» 10 раз и никогда не должен получать доступ к _ Переменная.

            for _ in range(10):
        print("I must not talk in class")
    
  • __var: переменные подчеркивания с двойным началом (минимум два символа подчеркивания в начале и не более одного символа подчеркивания в конце). При использовании в качестве атрибутов класса (переменных и методов) эти переменные подлежат изменению имени: вне класса python переименует атрибут в _<Class_name>__<attribute_name>. Пример:

            class MyClass:
        __an_attribute = "attribute_value"
    
    my_class = MyClass()
    print(my_class._MyClass__an_attribute)  # "attribute_value"
    print(my_class.__an_attribute)  # AttributeError: 'MyClass' object has no attribute '__an_attribute'
    

    При использовании в качестве переменных вне класса они ведут себя как одиночные переменные подчеркивания в начале.

  • __var__: двойные ведущие и завершающие переменные подчеркивания (как минимум два ведущих и завершающих символа подчеркивания). Также называется дундерс . Это соглашение об именах используется python для внутреннего определения переменных. Избегайте использования этого соглашения, чтобы предотвратить конфликты имен, которые могут возникнуть при обновлениях Python. Переменные Dunder ведут себя как одиночные переменные подчеркивания в начале: они не подвергаются искажению имени при использовании внутри классов, но не импортируются при импорте подстановочных знаков.

Подчеркнуть (_) в Python

Ниже приведены различные места, где _ используется в Python:

Единственный знак подчеркивания:

  • В переводчике
  • После имени
  • Перед именем

Двойной знак подчеркивания:

  • __leading_double_underscore

  • до после

  • Единственный знак подчеркивания

В переводчике:

_ возвращает значение последнего выполненного значения выражения в 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(__)

__leading_double_underscore

Двойное подчеркивание приводит к тому, что интерпретатор python переписывает имя, чтобы избежать конфликта в подклассе. Интерпретатор изменяет имя переменной с расширением класса и этой функцией, известной как Mangling. 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 Mangling измените имя переменной с помощью ___. Так что несколько раз он используется как закрытый член, потому что другой класс не может получить доступ к этой переменной напрямую. Основная цель для __ - использовать переменную / метод только в классе. Если вы хотите использовать его вне класса, вы можете сделать общедоступным 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() с передачей только getter. С помощью свойства () мы можем иметь полный контроль над данными.

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 задал немного другой вопрос, но так как я нашел другой вопрос, спрашивающий "как установить закрытые переменные", помеченный как дубликат этого, я подумал добавить здесь дополнительную информацию.

Отличные ответы, и все они правильные. Я привел простой пример вместе с простым определением / значением.

Имея в виду:

some_variable --► это публично, каждый может увидеть это.

_some_variable --► это публично, кто угодно может увидеть это, но это соглашение, указывающее частное...предупреждение, что Python не применяет никаких мер.

__some_varaible --► Python заменяет имя переменной на _classname__some_varaible (искажение имени AKA) и уменьшает / скрывает ее видимость и больше напоминает приватную переменную.

Просто чтобы быть честным здесь Согласно документации Python

"Закрытые" переменные экземпляра, к которым нельзя получить доступ, кроме как изнутри объекта, не существуют в Python "

Пример:

class A():
    here="abc"
    _here="_abc"
    __here="__abc"


aObject=A()
print(aObject.here) 
print(aObject._here)
# now if we try to print __here then it will fail because it's not public variable 
#print(aObject.__here)

Вот простой иллюстративный пример того, как свойства двойного подчеркивания могут влиять на унаследованный класс. Итак, со следующей настройкой:

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'

Это может быть очевидным для некоторых, но застало меня врасплох в гораздо более сложной среде

Одиночные ведущие подчеркивания - это соглашение. нет никакой разницы с точки зрения переводчика, если имена начинаются с одного подчеркивания или нет.

Двойные начальные и конечные подчеркивания используются для встроенных методов, таких как __init__, __bool__, так далее.

Двойное начальное подчеркивание без конечных аналогов также является соглашением, однако методы класса будут искажены интерпретатором. Для переменных или имен основных функций различий не существует.

Ваш вопрос хорош, речь идет не только о методах. Функции и объекты в модулях обычно имеют префикс с одним подчеркиванием и могут иметь префикс с двумя.

Но, например, имена __double_underscore не искажаются в модулях. Что происходит, так это то, что имена, начинающиеся с одного (или более) подчеркивания, не импортируются, если вы импортируете все из модуля (из модуля import *), а имена не отображаются в справке (module).

"Частные" переменные экземпляра, к которым нельзя получить доступ, кроме как изнутри объекта, не существуют в Python. Однако существует соглашение, которому следует большая часть кода Python: имя с префиксом подчеркивания (например, _spam) следует рассматривать как непубличную часть API (будь то функция, метод или элемент данных), Это следует считать деталями реализации и могут быть изменены без предварительного уведомления.

ссылка https://docs.python.org/2/tutorial/classes.html

Получить факты _ и __ довольно легко; другие ответы выражают их довольно хорошо. Использование намного сложнее определить.

Вот как я это вижу:

_

Должен использоваться для указания того, что функция не предназначена для публичного использования, как, например, API. Это и ограничение импорта заставляют его вести себя как internal в с #.

__

Следует использовать, чтобы избежать коллизии имен в иерархии наследования и избежать позднего связывания. Очень похоже на личное в C#.

==>

Если вы хотите указать, что что-то не для публичного использования, но оно должно действовать как protected использование _, Если вы хотите указать, что что-то не для публичного использования, но оно должно действовать как private использование __,

Это также цитата, которая мне очень нравится:

Проблема в том, что автор класса может на законных основаниях думать, что "это имя атрибута / метода должно быть закрытым, доступным только из определения этого класса" и использовать соглашение __private. Но позже пользователь этого класса может создать подкласс, который законно нуждается в доступе к этому имени. Таким образом, либо суперкласс должен быть изменен (что может быть трудным или невозможным), либо код подкласса должен использовать искаженные вручную имена (что в лучшем случае уродливо и хрупко).

Но проблема в том, что, на мой взгляд, если нет среды IDE, которая предупреждает вас при переопределении методов, обнаружение ошибки может занять некоторое время, если вы случайно переопределили метод из базового класса.

В случае с методами вы можете использовать двойное подчеркивание, чтобы скрыть частные «методы» со следующим шаблоном:

      # Private methods of MyClass
def _MyClass__do_something(obj:'MyClass'):
    print('_MyClass__do_something() called. type(obj) = {}'.format(type(obj)))

class MyClass():
    def __init__(self):
        __do_something(self)

mc = MyClass()

Выход:

      _MyClass__do_something() called. type(obj) = <class '__main__.MyClass'>

Я наткнулся на это сегодня, когда попытался использовать двойное подчеркивание для методов класса и получил NameError: name '_<class><method>' is not defined ошибка.

Чтобы выразить это простыми словами, давайте сравним соглашения о доступности переменных Python с модификаторами доступа в Java:

      (Python)                                     =   (Java)
_single_underscore_variable                  =   Protected (Accessible to class and its subclasses)
__double_underscore_variable                 =   Private (Accessible to class itself only)
no_underscore_variable                       =   Public (Accessible anywhere)

Ссылка : https://www.tutorialsteacher.com/python/public-private-protected-modifiers

Для людей, знакомых с Java, его поведение почти эквивалентно ключевому слову final в Java.

Другие вопросы по тегам