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