Вызывает "частную" переменную из родительского класса (как дочерний класс), нарушая инкапсуляцию

Я пытаюсь понять больше о возможностях переменных python.

На данный момент я не хочу ломать или нарушать инкапсуляцию переменных, которые объявлены как частные, т. Е. «self._variable».

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

class Parent:
    def __init__():
        self._randomVariable = ''
class Child(Parent):
    def__init__():
        super().__init__()
    def doSomething():
        self._randomVariable = 'Test'

Нарушает ли технически Chid.doSomething() инкапсуляцию для прямого вызова self._randomVariable в своем методе, даже если это дочерний класс?

Я не смог найти ничего, что было бы специфично для Python об инкапсуляции, а скорее вещи, основанные на Java. Это та же самая идея между Java и Python?

_randomValue — это атрибут protected. Так что нет. Если бы это было __randomValue, то это было бы.
rdas 29.05.2019 07:51

Можете ли вы уточнить, что означают защищенные атрибуты и в чем разница между двумя «_» и одним?

Ghost Ark 29.05.2019 07:54

кроме того, не забывайте - все эти _ и __ являются условностью - код будет работать и без них - они просто показывают ваше намерение читателю кода

Drako 29.05.2019 07:54

@Drako В случае _ да, это условность. Но не для __, так как python будет изменять имена для этих атрибутов и пытаться их скрыть.

rdas 29.05.2019 07:55

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

Ghost Ark 29.05.2019 07:55

@GhostArk Прочтите это: tutorialsteacher.com/python/…

rdas 29.05.2019 07:55

@rdas Подойдет! Большое спасибо за ваше время и ресурсы!

Ghost Ark 29.05.2019 07:56

@ Драко: не совсем так. _ атрибуты просто помечены как защищенные, а __ имеют свое имя искалеченный с именем класса. Поэтому, когда дочерний класс использует переменную _ от своего родителя, они имеют один и тот же атрибут, в то время как с __ они будут другими. По этой причине _ считается защищенным, а __ — частным.

Serge Ballesta 29.05.2019 07:57

true @rdas - я имел в виду, что вы часто можете видеть код без _ и __ - все еще работающий.

Drako 29.05.2019 07:58
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
9
644
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Инкапсуляция не так важна в Python, как в большинстве других языков (Java, C++ и т. д.), и вам действительно не следует слишком беспокоиться об этом. В сообществе Python у нас есть такой принцип, что «мы все здесь взрослые по обоюдному согласию».

Это означает, что вы несете ответственность за то, чтобы возиться с чужим кодом, но также не запрещайте другим возиться с вашим кодом, если они действительно знают, что делают. По этой причине в Python на самом деле нет private и protected, и вам не следует беспокоиться о них так же, как в Java.

Но, как теперь стало ясно, с подчеркиванием все же есть какая-то конфиденциальность. Итак, для чего они обычно используются?

Переменные с одним подчеркиванием (например, self._var)

Они используются для обеих частных защищенных переменных а также. Префикс вашей переменной с подчеркиванием - это (в основном) соглашение, который просто сообщает читателю, что «эта переменная используется внутри этого класса и не должна быть доступна извне». Что ж, если вашим подклассам это нужно, они все равно могут его использовать. И если вам это нужно вне класса, вы все равно можете его использовать. Но это под вашу ответственность, убедитесь, что вы ничего не сломаете.

Есть и некоторые другие незначительные эффекты, такие как from module import * отсутствие импорта переменных с префиксом подчеркивания, но соглашение о конфиденциальности является основным моментом.

Переменные с двойным подчеркиванием (например, self.__var)

Также известные как переменные «dunder» (двойные под), они используются для искажение имени. Идея состоит в том, что если у вас есть общее имя переменной, и вы боитесь, что подклассы могут использовать одно и то же имя переменной для своих внутренних вещей, вы можете использовать префикс двойного подчеркивания, чтобы защитить вашу переменную от случайной перезаписи. Таким образом, ваш self.__var станет self._BaseClassName__var, а self.__var вашего подкласса станет self._SubClassName__var. Теперь они не будут пересекаться.

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

Прежде всего, позвольте мне немного изменить вашу программу, чтобы исправить некоторые проблемы:

class Parent:
    def __init__(self):
        self.__randomVariable = 'Test1'
class Child(Parent):
    def __init__(self):
        super().__init__()
    def doSomething(self):
        self.__randomVariable = 'Test2'

Я добавил self к методам в качестве первых аргументов, так как это требуется при определении метода.

Я изменил назначение в родительском классе на 'Test1' вместо '' просто для примера.

Я также использовал __randomVariable с двойным подчеркиванием, так как ваш вопрос касается закрытых переменных, а для закрытых переменных требуется два подчеркивания. В Python частные переменные на самом деле не являются частными, но используется механизм «изменения имен», чтобы превратить их в переменную с именем _Parent.__randomVariable или _Child.__randomVariable в зависимости от класса, в котором они объявляются.

По поводу размаха. Если бы вы ссылались на self.__randomVariable, это относилось бы только к частной переменной с именем __randomVariable, как определено в этом классе. На самом деле в вашем случае было бы две разные такие частные переменные. Был бы один, определенный в суперклассе, который из-за механизма «искажения имен» фактически сохраняется как _Parent.__randomVariable. И есть один, определенный в подклассе, который сохраняется как _Child.__randomVariable. Итак, в вашем примере ваш дочерний класс фактически НЕ обращается к переменной из своего родительского класса; вместо этого он определяет новую приватную переменную для экземпляра дочернего класса.

Вот пример кода, чтобы проиллюстрировать, что произойдет, если вы используете приведенные выше определения:

c = Child()
print(c._Parent__randomVariable)
# prints Test1, i.e. the value assigned in the parent class
c.doSomething()
# calling doSomething will execute the assignment in the subclass
print(c._Parent__randomVariable)
# still prints Test1, i.e. the private variable in the parent class
# did not get reassigned in the method doSomething
print(c._Child__randomVariable)
# This one prints Test2, so in fact what happened is that a new
# private attribute was created in the subclass.

Следовательно, в этом фрагменте кода нет «нарушения инкапсуляции», поскольку он создает разные частные переменные экземпляра в родительском и дочернем классах. При этом частные переменные экземпляра на самом деле не «инкапсулированы», поскольку вы всегда можете получить к ним доступ (даже если вы не должны этого делать), когда вы знаете механизм искажения имен. Вот что я сделал в приведенном выше примере кода: я получил доступ к частной переменной __randomVariable родительского класса и подкласса, написав c._Parent__randomVariable соответственно c._Child__randomVariable. Это просто трюк реализации, который Python использует для имитации частных переменных.

Другие вопросы по теме