Chapter 8: Object-oriented programming

Encapsulation

face Josiah Wang

Let’s illustrate encapsulation in Python with an example. Here is an example code from before.

1
2
3
4
5
6
7
8
9
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

person = Person("Josiah Wang", 20)  
print(person.age)
person.age = 70
print(person.age)

Let’s say we now decided that we do not want a person’s age to be exposed directly. Instead, a person will always publicly declare themselves to be two years younger than their real age! We might also allow a person’s age to be modified, but only if the new age is less than 30.

If we were to think like a C++ programmer, then we might end up with Python code that looks like this.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Person:
    def __init__(self, name, age):
        self.name = name
        self.__age = age

    def get_age(self):
        return self.__age - 2

    def set_age(self, new_age):
        if new_age < 30:
            self.__age = new_age
        else:
            print("Never! I am always under 30!")


person = Person("Josiah Wang", 20)  
print(person.get_age())
person.set_age(10)
print(person.get_age())
person.set_age(70)
print(person.get_age())
print(person.__age)  ## this won't work!

In Python, you make an attribute (or method) non-public by prepending two underscores, e.g. self.__age (Line 4). This will activate name mangling which will hide this attribute from outside the class definition (so line 22 does not work). But you can actually still access this if you are desperate enough (with person._Person__age)! Don’t do this though, there is probably a clear reason why the original developer does not want you to use this variable in the first place!

Ignoring line 22, the code works. The real problems (from Python’s point) are:

  1. person.get_age() and person.set_age() make the code less readable in Lines 17-21 than simply just person.age in our original version.
  2. Everybody who have previously used the original Person class will have to modify all occurrences of person.age to person.get_age() and person.set_age().

Is there a better solution to this? In the next page, I will show the Pythonic way to approach this.