Есть ли у Python “приватные” переменные в классах?
Я пришел из мира Java и читаю книгу Брюса Экельса "Шаблоны, рецепты и идиомы Python 3".
Далее, читая о классах, говорится, что в 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?
Переведено автоматически
Ответ 1
Это культурно. В Python вы не выполняете запись в экземпляры других классов или переменные класса. В Java ничто не мешает вам сделать то же самое, если вы действительно хотите - в конце концов, вы всегда можете отредактировать исходный код самого класса для достижения того же эффекта. Python отбрасывает эту претензию на безопасность и призывает программистов быть ответственными. На практике это работает очень хорошо.
Если вы по какой-то причине хотите эмулировать приватные переменные, вы всегда можете использовать __
префикс из PEP 8. Python искажает имена переменных типа __foo
, чтобы они были нелегко видны коду за пределами пространства имен, которое их содержит (хотя вы можете обойти это, если будете достаточно решительны, точно так же, как вы можете обойти защиту Java, если поработаете над этим).
По тому же соглашению, _
префикс означает, что _variable
должен использоваться только внутри класса (или модуля), даже если технически вам не запрещен доступ к нему откуда-либо еще. Вы не играете с переменными другого класса, которые выглядят как __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
В 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 без переопределения родительских переменных. Но ничто не мешает вам обращаться к переменным, начинающимся с двойного подчеркивания. Однако искажение имен не позволяет вам случайно вызывать эти переменные / методы.
Я настоятельно рекомендую вам посмотреть книгу Раймонда Хеттингера Python's class development toolkit от PyCon 2013, в которой приводится хороший пример, почему и как следует использовать @property и "__"-переменные экземпляра (соответствующий раздел начинается с 37 минут 16 секунд).
Если у вас есть открытые переменные и вам необходимо их инкапсулировать, вы можете использовать @property . Следовательно, вы можете начать с самого простого возможного решения. Вы можете оставить переменные-члены общедоступными, если у вас нет конкретной причины этого не делать. Вот пример:
class Distance:
def __init__(self, meter):
self.meter = meter
d = Distance(1.0)
print(d.meter)
# prints 1.0
class Distance:
def __init__(self, meter):
# Customer request: Distances must be stored in millimeters.
# Public available internals must be changed.
# This would break client code in C++.
# This is why you never expose public variables in C++ or Java.
# However, this is Python.
self.millimeter = meter * 1000
# In Python we have @property to the rescue.
@property
def meter(self):
return self.millimeter *0.001
@meter.setter
def meter(self, value):
self.millimeter = value * 1000
d = Distance(1.0)
print(d.meter)
# prints 1.0