Does Python have “private” variables in classes?
I’m coming from the Java world and reading Bruce Eckels’ Python 3 Patterns, Recipes, and Idioms. While reading about classes, it goes on to say that in Python there is no need to declare instance variables. You just use them in the constructor, and boom, they are there.
So for example:
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())
If that’s true, then any object of class Simple can just change the value of variable s
outside of the class.
For example:
if __name__ == "__main__":
x = Simple("constructor argument")
x.s = "test15" # this changes the value
x.show()
x.showMsg("A message")
In Java, we have been taught about public/private/protected variables. Those keywords make sense because at times you want variables in a class to which no one outside the class has access to.
Why is that not required in Python? Are there any alternatives to achieve this concept of python private variables?
Python does not have true private variables like Java. However, it uses a technique called “name mangling” to simulate private variables.
By convention, variables with a double underscore prefix (e.g., self.__s) are treated as private, meaning they can’t be easily accessed directly from outside the class. The interpreter renames the variable in the background, making it harder to access.
Example:
class Simple:
def __init__(self, s):
self.__s = s # This is a "private" variable
def show(self):
print(self.__s)
obj = Simple("constructor argument")
# print(obj.__s) # This will raise an AttributeError
You can use the property() function or the @property decorator to control access to instance variables, allowing you to implement getter and setter methods that give you more control over how the variables are accessed or modified.
Example:
class Simple:
def __init__(self, s):
self._s = s
@property
def s(self):
return self._s
@s.setter
def s(self, value):
if isinstance(value, str): # You can add validation logic
self._s = value
else:
raise ValueError("Must be a string")
obj = Simple("constructor argument")
obj.s = "new value" # Works fine
print(obj.s)
Another way to manage access to instance variables is by exposing them through methods, rather than directly accessing them. This gives you complete control over what happens when the variable is read or modified.
Example:
class Simple:
def __init__(self, s):
self._s = s
def get_s(self):
return self._s
def set_s(self, value):
if isinstance(value, str):
self._s = value
else:
raise ValueError("Must be a string")
obj = Simple("constructor argument")
print(obj.get_s())
obj.set_s("new value")
print(obj.get_s())