Что означает одиночное и двойное подчеркивание перед именем объекта? - PullRequest
1137 голосов
/ 19 августа 2009

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

Ответы [ 14 ]

1021 голосов
/ 19 августа 2009

Single Underscore

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

Цитировать PEP-8 :

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

Двойное подчеркивание (искажение имени)

С Документация по Python :

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

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

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

* +1032 * Пример
>>> 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!'}
285 голосов
/ 19 августа 2009

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

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

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

273 голосов
/ 19 августа 2009

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

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

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

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

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

190 голосов
/ 28 сентября 2012

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

100 голосов
/ 15 декабря 2014

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

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 .

15 голосов
/ 11 января 2012

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

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

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

from sphinx.locale import l_, _

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

7 голосов
/ 15 апреля 2013

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

5 голосов
/ 19 августа 2009

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

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

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

3 голосов
/ 22 января 2019

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

Значение:

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)
3 голосов
/ 07 февраля 2015

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

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

...