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

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

13 ответов


Одного Символа Подчеркивания

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

цитата PEP-8:

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

двойная Подчеркивание (Имя Искажение)

С документы Python:

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