As we mentioned earlier, the property descriptor is a mechanism for triggering function calls while accessing the attributes of an object.
In our example, they wrap access to the internal attributes with a leading underscore, like so:
contact.first_name = "John" # Stores "John" in contact._first_name
print(contact.first_name) # Reads "John" from contact._first_name
contact.last_name = "" # ValueError raised by the required function
The property descriptor is typically used with the @decorated syntax—remember to always use the same name for the decorated functions:
@property
def last_name(self):
# ...
@last_name.setter
def last_name(self, value):
# ...