Chapter 3: Using decorators

Property decorator

face Josiah Wang

You will most likely also be using decorators provided by Python.

The main one that you might have encountered is the @property decorator. We discussed this in the Advanced OOP lesson.

Here is our version of the code that uses the @property and @property_name.setter decorator to encapsulate VainPerson’s name. I have simplified the setter method in this version just to make it easier to read.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class VainPerson:
    def __init__(self, name):
        self.name = name

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

    @name.setter
    def name(self, new_name):
        self.__name = new_name.upper()

lovely_person = VainPerson("Edward")
print(lovely_person.name) # Your Most Honourable Excellency EDWARD
lovely_person.name = "george"
print(lovely_person.name) # Your Most Honourable Excellency GEORGE

When you use the @property and @property_name.setter decorators, Python automatically converts them to something like the following code. Here, we create a property object called name (Line 11), and link the property to the getter method get_name() and the setter method set_name().

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
class VainPerson:
    def __init__(self, name):
        self.set_name(name)

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

    def set_name(self, new_name):
        self.__name = new_name.upper()

    name = property(get_name, set_name)

lovely_person = VainPerson("Edward")
print(lovely_person.name) # Your Most Honourable Excellency EDWARD
lovely_person.name = "george"
print(lovely_person.name) # Your Most Honourable Excellency GEORGE

The property class has a constructor, which takes a function (the getter method) as the first parameter, and optionally a setter method as the second parameter. Python will automatically invoke the correct methods when a user accesses the .name attribute of a VainPerson instance.

If you omit the setter method (the second argument), then your property becomes a read-only property.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class VainPerson:
    def __init__(self, name):
        self.set_name(name)

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

    def set_name(self, new_name):
        self.__name = new_name.upper()

    name = property(get_name)

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