Appearance
Access Restriction
In a class, attributes and methods can be defined, allowing external code to manipulate data through instance variable methods, thereby hiding complex internal logic.
However, in the previous definition of the Student class, external code can freely modify an instance's name
and score
attributes:
python
>>> bart = Student('Bart Simpson', 59)
>>> bart.score
59
>>> bart.score = 99
>>> bart.score
99
To prevent external access to internal attributes, prefix the attribute name with two underscores __
. In Python, instance variable names that start with __
become private, accessible only internally. Therefore, we modify the Student class:
python
class Student(object):
def __init__(self, name, score):
self.__name = name
self.__score = score
def print_score(self):
print('%s: %s' % (self.__name, self.__score))
After this change, external code cannot access instance variables .__name
and .__score
:
python
>>> bart = Student('Bart Simpson', 59)
>>> bart.__name
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Student' object has no attribute '__name'
This ensures that external code cannot arbitrarily modify the object's internal state, making the code more robust through access restriction.
What if external code needs to access name
and score
? We can add get_name
and get_score
methods to the Student class:
python
class Student(object):
...
def get_name(self):
return self.__name
def get_score(self):
return self.__score
If we want to allow external code to modify score
, we can add a set_score
method:
python
class Student(object):
...
def set_score(self, score):
self.__score = score
You might wonder why we don't just use bart.score = 99
directly; we define a method to validate the parameter, preventing invalid input:
python
class Student(object):
...
def set_score(self, score):
if 0 <= score <= 100:
self.__score = score
else:
raise ValueError('bad score')
Note that in Python, variable names like __xxx__
, which begin and end with double underscores, are special variables and can be accessed directly; they are not private. Therefore, avoid using names like __name__
or __score__
.
Sometimes you may see instance variable names starting with a single underscore, like _name
. Such variables are accessible from outside, but conventionally, it signals that they should be treated as private and not accessed directly.
Are double-underscore variables completely inaccessible from outside? Not exactly. You cannot directly access __name
because the Python interpreter changes it to _Student__name
, allowing access like this:
python
>>> bart._Student__name
'Bart Simpson'
However, it's strongly advised against doing this, as different Python interpreter versions may change the variable name differently.
In summary, Python itself does not have mechanisms to prevent misuse; it's all about self-discipline.
Finally, be aware of this incorrect usage:
python
>>> bart = Student('Bart Simpson', 59)
>>> bart.get_name()
'Bart Simpson'
>>> bart.__name = 'New Name' # Setting __name variable!
>>> bart.__name
'New Name'
At first glance, it seems external code "successfully" set the __name
variable, but this __name
is not the same as the internal one! The internal __name
has been automatically changed by the Python interpreter to _Student__name
, while external code added a new __name
variable. Try it:
python
>>> bart.get_name() # get_name() returns self.__name
'Bart Simpson'
Exercise
Please hide the gender
field of the Student object from external access, using get_gender()
and set_gender()
methods, and validate the parameters:
python
class Student(object):
def __init__(self, name, gender):
self.name = name
self.gender = gender
# Test:
bart = Student('Bart', 'male')
if bart.get_gender() != 'male':
print('Test failed!')
else:
bart.set_gender('female')
if bart.get_gender() != 'female':
print('Test failed!')
else:
print('Test succeeded!')