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




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

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


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

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

Процитировать 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!'}

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

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


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

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'

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


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

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

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


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

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

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

from sphinx.locale import l_, _

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


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

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

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


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

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

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

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

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


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

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

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-файле .


Подчеркивание (_) в 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

Reference







double-underscore