Python language basics 74: validating class level properties
December 20, 2015 2 Comments
In the previous post we discussed class level properties in some detail. The most important detail we learned is that there are only public members in a Python class. Properties can be added to “self” on the fly within the class but those properties will be visible to all external callers. So all you’ve learnt about access modifiers from your Java course, such as private or public is not applicable in Python.
In this post we’ll explore the basics of validating the values that are sent into the object initialiser. Validation is a large topic so consider this only as a light introduction.
Before we continue I’d like to come back a little bit to the absence of private variables in Python. It’s important to understand why private variables exist in the first place and why it’s a bit difficult to accept their absence for a staunch C# and Java developer like myself.
Private variables of a class can be likened to your private belongings, such as your wallet. I assume that your wallet has at least some cash in it and possibly a credit card, an ID card and some other important things. You’d probably not leave your wallet in some public place for everyone to search through it. You may be lucky though. It’s possible that no-one takes your wallet and anything in it. However, it’s far from guaranteed.
If you were to model this Wallet class then it may have properties such as cash, ID and credit_card. You’d prefer to control external access to them, right? Just like you’d like to determine how the things enter and leave your wallet. In Java/C#/etc. you’d hide those properties with the private access modifier and write specialised methods that can internally modify the private variables according to some rules.
So if you cannot hide those properties in Python then a second option is to at least validate their initial value within the init function. Validation and the idea of class invariants go hand in hand. Class invariants are the rules of the class. E.g. a price cannot be negative, a person’s name cannot include funny characters like “%&/”, an email address must contain one and only one “@” etc.
Let’s extend the Person class with an _age property:
class Person: def __init__(self, name, age): self._name = name self._age = age def whats_your_name(self): return self._name def describe_me(self): return "My name is " + self._name + " and I am " + str(self._age) + " years old." def shout(self): print("HELLOOOOO")
Example of usage:
person = Person("Elvis", 40) print(person.describe_me())
…which prints “My name is Elvis and I am 40 years old.”.
However, we can easily provide unacceptable values to the initialisator:
person = Person("", -10)
We can extend the init function and throw an exception if the assigned values are incorrect. Let’s set up the following rules:
- A person’s name cannot be an empty string
- A person’s age must be between 0 and 120
Here’s a possible solution:
class Person: def __init__(self, name, age): if name == "" or str(name).isspace(): raise ValueError("Person's name must not be empty.") if not str(age).isdigit(): raise ValueError("Age must be a number.") if age < 0 or age > 120: raise ValueError("Age must lie between 0 and 120.") self._name = name self._age = age def whats_your_name(self): return self._name def describe_me(self): return "My name is " + self._name + " and I am " + str(self._age) + " years old." def shout(self): print("HELLOOOOO")
Examples where an exception will be raised:
person = Person("", 15) person = Person(" ", 34) person = Person("John", 150)
As noted above validation is a large topic. The goal is to check the values sent into the init function so that the object created doesn’t get into an invalid state.
In the next post we’ll look into class level methods.
Read all Python-related posts on this blog here.