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


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

13 1015

13 ответов:

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

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

цитата PEP-8:

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

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

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

любой идентификатор формы __spam (по крайней мере два ведущих подчеркивания, не более одного завершающего подчеркивания) текстуально заменяется на _classname__spam, где classname - это текущее имя класса с начальным подчеркиванием. Это искажение выполняется без учета синтаксической позиции идентификатора, поэтому его можно использовать для определения класса-частного экземпляра и переменных класса, методов, переменные, хранящиеся в глобалах, и даже переменные, хранящиеся в экземплярах. частный для этого класса на экземплярах других классов.

и предупреждение с той же страницы:

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

пример

>>> 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, одна очередь импорта, очередь подклассов.Очередь, и переопределяет такие методы, как _get и _put; "клиентский код" никогда не вызывает эти ("крючок") методы, а скорее ("организация") общедоступные методы, такие как put и get (это известно как Метод Шаблона шаблон -- см., например,здесь для интересной презентации, основанной на видео моего разговора на эту тему, с добавление конспектов стенограммы).

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

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

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

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

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

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

.__variable часто неправильно считается суперприватным, в то время как его фактическое значение-это просто namemangle to предотвратить случайный доступ[1]

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

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

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

в интерпретаторе Mangling 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

_ _ BEFORE_AFTER__

имя с началом С __ и заканчивается тем же рассматривает специальные методы в 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 лучшим способом было бы использовать свойство() с переданным ему только геттером. С имущества() мы можем иметь полный контроль над данными.

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__ и т. д.

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

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

но _ _ double _ underscore имена не являются имя-mangled в модулях, например. Что происходит, так это то, что имена, начинающиеся с одного (или более) подчеркивания, не импортируются, если вы импортируете все из модуля (из импорта модуля*), а также имена, показанные в справке(модуль).

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

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 inheritace и избежать позднего связывания. Очень похожий частный в c#.

==>

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

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

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

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