Chapter 3: Encapsulation

Property

face Josiah Wang

In our previous code, get_name() is clunky and hard to read. It would have been nicer if we could just simply use the following as before.

print(lovely_person.name)

Can we still access the name attribute this way, but without exposing the attribute?

The answer is yes. All we need to do is to convert the getter method get_name() into a name property.

More specifically, you rename get_name() to name() (Line 6) and annotate the method with a @property decorator (Line 5).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class VainPerson:
    def __init__(self, name):
        self.set_name(name)

    @property
    def name(self):
        return f"Your Most Honourable Excellency {self.__name}"

    def set_name(self, name):
        if self.likes_name(name):
            self.__name = name.upper()
        else:
            if self.__name is None:
                # Just so we have a default name
                self.__name = "JOSIAH"
            else:
                print("I doth not liketh yond nameth. Off with thy headeth!")

    def likes_name(self, name):
        if name.upper() in ["EDWARD", "VICTORIA", "WILLIAM", "GEORGE",
                            "ELIZABETH", "JOSIAH"]:
            return True
        else:
            return False

lovely_person = VainPerson("Edward")
print(lovely_person.name) # Your Most Honourable Excellency EDWARD
lovely_person.name = "George" # AttributeError: can't set attribute

The @property decorator essentially converts the name() method to become the name property, so that you can use it like an attribute. We will talk about what really happens ‘behind the scenes’ when we discuss decorators in the future. It is enough to know that it converts a method to a property!

You can also see that Line 28 does not work because name is a read-only property.