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

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

Ответы [ 14 ]

3 голосов
/ 22 августа 2014

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

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'

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

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

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

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

2 голосов
/ 20 октября 2018

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

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

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.

0 голосов
/ 04 ноября 2017

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

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

_

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

__

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

==>

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

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

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

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

...