Есть ли в Python «закрытые» переменные в классах? - PullRequest
490 голосов
/ 29 октября 2009

Я из мира Java и читаю Шаблоны, рецепты и идиомы Брюса Экелса

Читая о классах, можно сказать, что в Python нет необходимости объявлять переменные экземпляра. Вы просто используете их в конструкторе, и бум, они там.

Так, например:

class Simple:
    def __init__(self, s):
        print("inside the simple constructor")
        self.s = s

    def show(self):
        print(self.s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Если это так, то любой объект класса Simple может просто изменить значение переменной s вне класса.

Например:

if __name__ == "__main__":
    x = Simple("constructor argument")
    x.s = "test15" # this changes the value
    x.show()
    x.showMsg("A message")

В Java нас учили о публичных / приватных / защищенных переменных. Эти ключевые слова имеют смысл, потому что иногда вам нужны переменные в классе, к которым никто за пределами класса не имеет доступа.

Почему это не требуется в Python?

Ответы [ 11 ]

867 голосов
/ 29 октября 2009

Это культурно. В Python вы не пишете в экземпляр класса или переменные класса. В Java ничто не мешает вам делать то же самое, если вы действительно хотите - в конце концов, вы всегда можете отредактировать исходный код самого класса для достижения того же эффекта. Python отказывается от претензий на безопасность и поощряет программистов к ответственности. На практике это работает очень хорошо.

Если по какой-то причине вы хотите эмулировать закрытые переменные, вы всегда можете использовать префикс __ из PEP 8 . Python изменяет имена переменных, например __foo, так что они не легко видны для кода вне класса, который их содержит (хотя вы можете обойти это, если вы достаточно решительны, как вы может обойти защиту Java, если вы над ней работаете).

В соответствии с тем же соглашением префикс _ означает, что остается в стороне, даже если вам технически не запрещается делать это . Вы не играете с переменными другого класса, которые выглядят как __foo или _bar.

130 голосов
/ 27 сентября 2015

Закрытые переменные в python более или менее полезны: интерпретатор намеренно переименовывает переменную.

class A:
    def __init__(self):
        self.__var = 123
    def printVar(self):
        print self.__var

Теперь, если вы попытаетесь получить доступ к __var вне определения класса, произойдет сбой:

 >>>x = A()
 >>>x.__var # this will return error: "A has no attribute __var"

 >>>x.printVar() # this gives back 123

Но вы можете легко сойти с рук:

 >>>x.__dict__ # this will show everything that is contained in object x
               # which in this case is something like {'_A__var' : 123}

 >>>x._A__var = 456 # you now know the masked name of private variables
 >>>x.printVar() # this gives back 456

Вы, вероятно, знаете, что методы в ООП вызываются следующим образом: x.printVar() => A.printVar(x), если A.printVar() может получить доступ к некоторому полю в x, к этому полю также можно получить доступ за пределами A.printVar() .. В конце концов, функции созданы для возможности повторного использования, внутри операторов нет особой власти.

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

24 голосов
/ 17 ноября 2014

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

20 голосов
/ 29 октября 2009

«В Java нас учили о публичных / приватных / защищенных переменных»

«Почему это не требуется в python?»

По той же причине не требуется в Java.

Вы можете использовать - или не использовать private и protected.

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

Почему бы и нет?

Вот мой вопрос "защищен от кого?"

Другие программисты в моей команде? У них есть источник. Что означает защищенный, когда они могут изменить его?

Другие программисты в других командах? Они работают на одну компанию. Они могут - с помощью телефонного звонка - получить источник.

Клиенты? Это программирование по найму (как правило). Клиенты (как правило) владеют кодом.

Итак, от кого именно я это защищаю?

14 голосов
/ 02 сентября 2014

В соглашении о подчеркивании есть вариация приватных переменных.

In [5]: class Test(object):
   ...:     def __private_method(self):
   ...:         return "Boo"
   ...:     def public_method(self):
   ...:         return self.__private_method()
   ...:     

In [6]: x = Test()

In [7]: x.public_method()
Out[7]: 'Boo'

In [8]: x.__private_method()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-8-fa17ce05d8bc> in <module>()
----> 1 x.__private_method()

AttributeError: 'Test' object has no attribute '__private_method'

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

Существуют примеры декораторов @private, которые более близко реализуют эту концепцию, но YMMV. Можно также написать определение класса, которое использует мета

9 голосов
/ 14 апреля 2017

Как упоминалось ранее, вы можете указать, что переменная или метод являются закрытыми, добавив префикс подчеркивания. Если вам кажется, что этого недостаточно, вы всегда можете использовать декоратор property. Вот пример:

class Foo:

    def __init__(self, bar):
        self._bar = bar

    @property
    def bar(self):
        """Getter for '_bar'."""
        return self._bar

Таким образом, кто-то или что-то, что ссылается на bar, фактически ссылается на возвращаемое значение функции bar, а не на саму переменную, и поэтому к нему можно получить доступ, но нельзя изменить. Однако, если кто-то действительно хочет, он может просто использовать _bar и присвоить ему новое значение. Не существует надежного способа запретить кому-либо доступ к переменным и методам, которые вы хотите скрыть, как уже неоднократно говорилось. Тем не менее, использование property - самое ясное сообщение, которое вы можете отправить, что переменная не подлежит редактированию. property также может использоваться для более сложных путей доступа для получения / установки / удаления, как описано здесь: https://docs.python.org/3/library/functions.html#property

9 голосов
/ 29 октября 2009

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

Подробнее об этом см. здесь .

В общем, реализация объектной ориентации в Python немного примитивна по сравнению с другими языками. Но на самом деле мне это нравится. Это очень концептуально простая реализация, которая хорошо сочетается с динамическим стилем языка.

8 голосов
/ 28 апреля 2013

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

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

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

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

7 голосов
/ 17 июля 2011

частные и защищенные концепции очень важны. Но python - всего лишь инструмент для создания прототипов и быстрой разработки с ограниченными ресурсами, доступными для разработки, поэтому некоторые уровни защиты не так строги в Python. Вы можете использовать «__» в классе, он работает правильно, но выглядит недостаточно хорошо - каждый доступ к такому полю содержит эти символы.

Кроме того, вы можете заметить, что концепция ООП Python не идеальна, смолтальк или рубин намного ближе к концепции чистого ООП. Даже C # или Java ближе.

Python - очень хороший инструмент. Но это упрощенный язык ООП. Синтаксически и концептуально упрощено. Основная цель существования Python - предоставить разработчикам возможность быстро и легко писать легко читаемый код с высоким уровнем абстракции.

5 голосов
/ 15 августа 2017

Извините, ребята, за "воскрешение" темы, но, надеюсь, это кому-нибудь поможет:

В Python3, если вы просто хотите «инкапсулировать» атрибуты класса, как в Java, вы можете просто сделать то же самое, как это:

class Simple:
    def __init__(self, str):
        print("inside the simple constructor")
        self.__s = str

    def show(self):
        print(self.__s)

    def showMsg(self, msg):
        print(msg + ':', self.show())

Чтобы создать это, сделайте:

ss = Simple("lol")
ss.show()

Обратите внимание: print(ss.__s) выдаст ошибку.

На практике Python3 запутывает глобальное имя атрибута. Превращая это как "приватный" атрибут, как в Java. Имя атрибута по-прежнему глобально, но недоступно, как частный атрибут в других языках.

Но не бойся этого. Это не важно Это тоже делает работу. ;)

...