Python language basics 76: class property setters
January 17, 2016 Leave a comment
Introduction
In the previous post we looked at class level methods. We saw that they weren’t much different from “normal” methods. The fact that the first input parameter is “self” is what differentiates class level methods. Also, they can be called on an instance of a class, i.e. an instantiated object. The extra properties that the method needs are provided after “self”.
In this post we’ll look at a special group of class methods: setters.
Setters
We saw before that there is no mechanism in Python to hide class properties:
class Person: def __init__(self, name, age): self._name = name self._age = age
The _name and _age properties will be accessible to external consumers of the class:
person = Person("John", 100) person._name = "Not John" print(person._name)
This prints “Not John”. In Python it’s customary to indicate class level properties with an underscore “_” in front. It’s a signal to other programmers that they should not directly access these elements.
How can the person’s name be modified then after the object initialisation step? If you’d like to make the _name field modifiable then you can create a class level method with the same name bar the underscore:
def name(self, name): self._name = name
Alternatively you can specifically call the method “set” followed by the property name:
def set_name(self, name): self._name = name
This is more like the Java way of doing it and may be frowned upon by Python experts.
Here comes a general note: setters and their “cousins”, the getters are not as straightforward to implement like in strictly typed languages like Java or C#. You’ll find multiple implementation types for Python if you search the Internet.
This is an example of a setter: a method designed to set the value of a class level variable. It’s really not different from normal class level methods but it has a different purpose. The above example is probably as easy as it gets as far as setters are concerned. The incoming value is simply assigned to the corresponding class level property. You are free to extend it of course, e.g. with some validation:
def name(self, name): if name == "" or str(name).isspace(): raise ValueError("Person's name must not be empty.") self._name = name
Bear in mind though that there may not be a setter for every property of a class. It’s not uncommon to let certain values of a class to be set once, usually through the initialiser and then never again during the object’s lifetime. If you don’t see a matching setter then it may as well mean that the property is either immutable or read-only or that the details of changing the value of the property are hidden within the class. External consumers are not supposed to directly access the “private” variables as there may be complex rules attached to how they are allowed to change.
E.g. consider the position of the front wheels of a car when turning. A Car object might have an initialiser to set the initial position of the wheels, i.e. their angles. However, it’s not wise to create setters for those wheels. It’s better to create other methods such as “turn_left” and “turn_right” that internally modify the angles of the front wheels. External callers of the class should not be able to modify the wheels’ position independently. The “turn” methods are analogous to turning the steering wheel. That operation is best implemented by the internal mechanics of the car. Imagine having to set the angles of the front wheels yourself. You would end up in an accident very quickly.
So always be careful whether or not it’s necessary to add setters to a class. Ask yourself whether the external caller can be wise enough to modify the value of a class level property.
In the next post we’ll look at the “cousin” of setters, i.e. the getters.
Read all Python-related posts on this blog here.