В чем смысл одиночного и двойного подчеркивания перед именем объекта?

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

Ответ 1

Одиночный подшерсток

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

Процитировать PEP-8:

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

Double Underscore (Name Mangling)

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

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

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

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

пример

>>> 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

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

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

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

Ответ 3

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

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

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

Никакой другой символ подчеркивания не имеет смысла в мире Python.

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

Ответ 4

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

.__variable часто неверно считается сверхприбыльным, в то время как фактическое значение - это просто для того, чтобы 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'

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

Ответ 5

Единственное подчеркивание в начале:

У 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

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

Ответ 6

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

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

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

from sphinx.locale import l_, _

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

Ответ 7

Подчеркивание (_) в Python

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

Single Underscore:

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

Double Underscore:

  • __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
>>>

Перед именем

Leading Underscore перед именем variable/function/method указывает программисту, что он предназначен только для внутреннего использования, который может быть изменен всякий раз, когда требуется класс.

Здесь префикс имени by underscore рассматривается как непубличный. Если указать из Import *, все имя начинается с _, не будет импортироваться.

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

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

Ссылка

Ответ 8

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

Ответ 9

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

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

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

Ответ 10

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

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

Ответ 11

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

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"

если затем создать дочерний экземпляр в REPL python, вы увидите ниже

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'

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

Ответ 12

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

ссылка https://docs.python.org/2/tutorial/classes.html#private-variables-and-class-local-references

Ответ 13

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

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

_

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

__

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

== >

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

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

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

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

Ответ 14

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

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

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.

Ответ 15

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

Имея в виду:

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 not public variable 
#print(aObject.__here)