Mistakes and errors happen in computer programs as much as in real life. Like life, how you handle an error in your program shows your level of professionalism, and gives others evidence that they can trust that you have written a program that will work well.
In the last section we indicated errors in the Person.setHeight
function by printing a message to the screen and returning False
to indicate that the call to setHeight
had failed.
class Person:
"""Class that holds a person's height"""
def __init__(self):
"""Construct a person who has zero height"""
self._height = 0
def setHeight(self, height):
"""Set the person's height to 'height', returning whether or
not the height was set successfully
"""
if height < 0 or height > 300:
print("This is an invalid height! %s" % height)
return False
else:
self._height = height
return True
def getHeight(self):
"""Return the person's height"""
return self._height
p = Person()
p.setHeight(-20)
This is not a good way of indicating an error. The issues with this are;
getHeight
know to check whether the call returns True
or False
The solution is to send something to the programmer that they cannot ignore, which indicates that there is an error. That something is called an "exception".
Take a look at this simple code that sets the height...
def setHeight(height):
if height < 0 or height > 300:
raise ValueError("Invalid height: %s. This should be between 0 and 300" % height)
print("Height is set to %s" % height)
setHeight(-5)
When we try to use an invalid value for the height, we raise (or throw) a ValueError
exception. This stops the function from continuing, and gives us a very helpful print out of what went wrong, and where.
ValueError
is just a class. The name of the class provides us with useful information (there was an error with a value in the program). You choose what error you want to raise. Python provides a set of usefully named error classes that you can use:
IOError
: Error raised when you have a problem with IO, e.g. opening or closing filesZeroDivisionError
: Error raised when you divide by zeroTypeError
: Error raised when you are using the wrong type, e.g. maybe setting the height to a stringIndexError
: Error raised when you are using an invalid index to access a list or other similar containerKeyError
: Error raised when you are using an invalid key to access a dictionary or other similar containerA full list of standard Python exceptions is available here.
You are free to raise any exception class you want. It is your job as a programmer to choose the one that is most sensible, e.g.
def setHeight(height):
if height < 0 or height > 300:
raise ZeroDivisionError("Invalid height: %s. This should be between 0 and 300" % height)
print("Height is set to %s" % height)
setHeight(400)
Using a ZeroDivisionError
is a bad choice, as the error has nothing to do with division by zero. A ValueError
is the right choice as the error relates to an invalid value passed to the function.
You are free to create your own exception classes.
class InvalidHeightError(Exception):
pass
def setHeight(height):
if height < 0 or height > 300:
raise InvalidHeightError("Invalid height: %s. This should be between 0 and 300" % height)
print("Height is set to %s" % height)
setHeight(-10)
Your own exception classes must be declared as derived from type Exception
, hence why you have to write class InvalidHeightError(Exception):
. As the class doesn't need to do anything else, you can use pass
to say that nothing else needs to be added. Note that you can call your error class anything you want. By convention, it is good to end the class name with Error
so that other programmers know what it is for.
Here is an extended version of the Person
code from the previous lesson.
class Person:
"""Class that holds a person's height"""
def __init__(self, height=0, weight=0):
"""Construct a person with the specified name, height and weight"""
self.setHeight(height)
self.setWeight(weight)
def setHeight(self, height):
"""Set the person's height in meters"""
self._height = height
def setWeight(self, weight):
"""Set the person's weight in kilograms"""
self._weight = weight
def getHeight(self):
"""Return the person's height in meters"""
return self._height
def getWeight(self):
"""Return the person's weight in kilograms"""
return self._weight
def bmi(self):
"""Return the person's body mass index (bmi)"""
return self.getWeight() / self.getHeight()**2