Имеет ли Python переменные "private" в классах?
Я прихожу из мира Java и читаю Bruce Eckels 'Python 3 Patterns, Recipes и Idioms.
Читая о классах, далее говорится, что в 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())
Если true, то любой объект класса Simple
может просто изменить значение переменной s
вне класса.
Например:
if __name__ == "__main__":
x = Simple("constructor argument")
x.s = "test15" # this changes the value
x.show()
x.showMsg("A message")
В Java нас изучают переменные public/private/protected. Эти ключевые слова имеют смысл, потому что иногда вам нужны переменные в классе, к которым не имеет доступа никто, кроме класса.
Почему это не требуется в Python?
Ответы
Ответ 1
Это культурный. В Python вы не записываете переменные экземпляра или класса другого класса. В Java ничто не мешает вам делать то же самое, если вы действительно этого хотите - в конце концов, вы всегда можете отредактировать источник самого класса для достижения такого же эффекта. Python отказывается от предлога безопасности и поощряет программистов нести ответственность. На практике это работает очень хорошо.
Если вы хотите по какой-то причине эмулировать частные переменные, вы всегда можете использовать префикс __
из PEP 8. Python управляет именами переменных, такими как __foo
, чтобы они не были легко видимы для кода вне класса, который их содержит (хотя вы можете обойти его, если вы достаточно уверены, так же, как вы можете обойти защиту Java, если вы работайте над ним).
В соответствии с тем же соглашением префикс _
означает держаться подальше, даже если вам не удается предотвратить это. Вы не играете с другими переменными класса, которые выглядят как __foo
или _bar
.
Ответ 2
Частные переменные в 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()
.... после того, как функции созданы для повторного использования, нет никакой особой силы, предоставляемой внутренним операторам.
Игра отличается при наличии компилятора (конфиденциальность - это концепция уровня компилятора). Он знает о определении класса с помощью модификаторов контроля доступа, чтобы он мог ошибиться, если во время компиляции правила не соблюдаются
Ответ 3
Как правильно упомянуто многими комментариями выше, давайте не будем забывать о главной цели модификаторов доступа: чтобы помочь пользователям кода понять, что должно измениться и чего не следует делать. Когда вы видите частное поле, вы не общаетесь с ним. Так что это в основном синтаксический сахар, который легко достигается в Python с помощью _ и __.
Ответ 4
"В Java нас учили о публичных/частных/защищенных переменных"
"Почему это не требуется в python?"
По той же причине это не требуется в Java.
Вы можете использовать или не использовать private
и protected
.
Как программист на Python и Java, я обнаружил, что private
и protected
являются очень и очень важными концепциями проектирования. Но на практике в десятках тысяч строк Java и Python я никогда не использовал private
или protected
.
Почему нет?
Вот мой вопрос "защищен от кого?"
Другие программисты в моей команде? У них есть источник. Что значит защищенный, когда они могут его изменить?
Другие программисты в других командах? Они работают на одну компанию. Они могут - с помощью телефонного звонка - получить источник.
Клиенты? Это программирование по найму (в общем). Клиенты (как правило) владеют кодом.
Так от кого именно я это защищаю?
Ответ 5
В контексте подчеркивания существует вариация частных переменных.
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. Возможно, можно было бы также написать определение класса, которое использует мета
Ответ 6
Python имеет ограниченную поддержку частных идентификаторов с помощью функции, которая автоматически добавляет имя класса к любым идентификаторам, начинающимся с двух символов подчеркивания. Это прозрачно для программиста, по большей части, но чистый эффект заключается в том, что любые переменные, названные таким образом, могут использоваться как частные переменные.
Подробнее см. здесь.
В целом, реализация ориентации объектов на Python немного примитивна по сравнению с другими языками. Но мне это нравится, на самом деле. Это очень концептуально простая реализация и хорошо сочетается с динамическим стилем языка.
Ответ 7
Как упомянуто ранее, вы можете указать, что переменная или метод являются закрытыми, добавив префикс подчеркивания. Если вам кажется, что этого недостаточно, вы всегда можете использовать декоратор 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
Ответ 8
Единственный раз, когда я когда-либо пользовался частными переменными, - это когда мне нужно делать другие вещи при записи или чтении из переменной, и поэтому мне нужно принудительно использовать установщик и/или getter.
Снова это касается культуры, как уже было сказано. Я работал над проектами, в которых чтение и запись переменных других классов были бесплатными для всех. Когда одна реализация стала устаревшей, потребовалось намного больше времени, чтобы идентифицировать все пути кода, которые использовали эту функцию. Когда принудительное использование сеттеров и геттеров было принудительно, выражение о отладке можно было легко записать, чтобы определить, что вызываемый метод был вызван, и путь к нему, который вызывает его.
Когда вы находитесь в проекте, где кто-либо может написать расширение, уведомление пользователей об устаревших методах, которые должны исчезнуть в нескольких выпусках, следовательно, имеет жизненно важное значение, чтобы свести модус к минимуму при обновлении.
Итак, мой ответ; если вы и ваши коллеги поддерживаете простой набор кодов, тогда защита переменных класса не всегда необходима. Если вы пишете расширяемую систему, тогда становится необходимым, когда сделаны изменения в ядре, которые необходимо уловить всеми расширениями с помощью кода.
Ответ 9
частные и защищенные концепции очень важны. Но python - просто инструмент для прототипирования и быстрой разработки с ограниченными ресурсами, доступными для разработки, поэтому некоторые уровни защиты не так строги, как в python. Вы можете использовать "__" в члене класса, он работает правильно, но выглядит недостаточно хорошо - каждый доступ к такому полю содержит эти символы.
Кроме того, вы можете заметить, что концепция OOP на Python не идеальна, smaltalk или рубин намного ближе к концепции чистого ООП. Даже С# или Java ближе.
Python - очень хороший инструмент. Но это упрощенный язык ООП. Синтаксически и концептуально упрощается. Основная цель существования python - предоставить разработчикам возможность очень легко писать легко читаемый код с высоким уровнем абстракции.
Ответ 10
Извините, парни за "воскрешение" темы, но, надеюсь, это кому-то поможет:
В 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. Имя атрибута по-прежнему является глобальным, но недоступным способом, как частный атрибут на других языках.
Но не бойтесь этого. Это не имеет значения. Он тоже выполняет эту работу. ;)
Ответ 11
Python не имеет каких-либо частных переменных, таких как C++ или Java. Вы также можете получить доступ к любой переменной-члену в любое время, если захотите. Однако вам не нужны частные переменные в Python, потому что в Python неплохо подвергать переменные-члены класса. Если вам нужно инкапсулировать переменную-член, вы можете сделать это, используя позже "@property", не нарушая существующий код клиента.
В python единственное подчеркивание "_" используется для указания того, что метод или переменная не рассматривается как часть публичного api класса и что эта часть api может изменяться между разными версиями. Вы можете использовать эти методы/переменные, но ваш код может сломаться, если вы используете более новую версию этого класса.
Двойное подчеркивание "__" не означает "частную переменную". Вы используете его для определения переменных, которые являются "локальными по классу" и которые не могут быть легко скрыты подклассами. Он управляет именем переменных.
Например:
class A(object):
def __init__(self):
self.__foobar = None # will be automatically mangled to self._A__foobar
class B(A):
def __init__(self):
self.__foobar = 1 # will be automatically mangled to self._B__foobar
self.__ имя foobar автоматически искажается для self._A__foobar в классе A. В классе B он искажен self._B__foobar. Поэтому каждый подкласс может определять свою собственную переменную __foobar без переопределения своих переменных (-ов) родителей. Но ничто не мешает вам получить доступ к переменным, начинающимся с двойных подчеркиваний. Тем не менее, управление именами не позволяет вам случайно вызвать эти переменные/методы.
Я настоятельно рекомендую посмотреть, как Раймонд Хеттингерс говорит "Набор инструментов для разработки классов Pythons" от Pycon 2013 (должен быть доступен на Youtube), что дает хороший пример, почему и как вы должны использовать переменные @property и "__" -instance.