Properties

YouTube Video

Video Materials

So far in this chapter we’ve learned how to create private and public attributes in our classes. What if we want to create an attribute that is read-only, or one that only accepts a particular set of values? In Python, we can use a special decorator @property to define special methods, called getters and setters, that can be used to access and update the value of private attributes.

Getter

In Python, a getter method is a method that can be used to access the value of a private attribute. To mark a getter method, we use the @property decorator, as in the following example:

class Property:
  
  def __init__(self):
    self.__name = ""    # create empty private attribute
    
  @property
  def name(self):
    return self.__name

In this class, the name attribute is private, so normally we wouldn’t be able to access its value. However, we’ve created a method name() that acts as a getter for the name private attribute. The decorator @property allows us to use that method as an attribute. In this way, the value of that variable can be accessed in a read-only fashion.

From other code, we can call that method by treating it just like an attribute called name:

prop = Property()
name = prop.name

Setter Methods

Similarly, we can create another method that can be used to update the value of the name attribute:

class Property:
  
  def __init__(self):
    self.__name = ""    # create empty private attribute
    
  @property
  def name(self):
    return self.__name
  @name.setter
  def name(self, value):
    if not isinstance(value, str):
      raise ValueError("Name must be a string")
    if len(value) == 0:
      raise ValueError("Name cannot be an empty string")
    self.__name = value

In this code, we’ve added another name() method below the decorator @name.setter that can be used to update the value stored in the name attribute. We’re also checking to make sure that the argument provided to the value parameter is a string, and is not an empty string. If it is, we can raise a ValueError, which would alert the user that this is not allowed. Of course, it would be up to the person writing the code that calls this method to properly catch and handle this exception.

In our other code, we can then update that value just like we would any other attribute:

prop = Property()
prop.name = "test"

UML Class Diagrams

Getter and setter methods are displayed on a UML class diagram just like any other method. We use naming conventions such as name() and name(value: str) to make it clear that those methods are getters and setters for the attribute name of type str, as in this UML class diagram:

UML Class Diagram with Properties UML Class Diagram with Properties

Try It!

So, through the use of getter and setter methods, along with the @property decorators, we can either prevent other code from updating an attribute, or enforce restrictions on that attribute’s values, without actually exposing the attribute. Here’s a sample main class that demonstrates how to use these properties:

from Property import *

class Main:
  
  @staticmethod
  def main():
    prop =  Property()
    name = prop.name
    print(name)
    prop.name = "test"
    print(prop.name)
    
    
if __name__ == "__main__":
    Main.main()