Can you please help me with the following. I am trying to catch two exceptions: 1) TypeError
and 2)NameError
. I use the following code below that estimates the average:
def calculate_average(number_list):
try:
if type(number_list) is not list:
raise ValueError("You should pass list to this function")
except ValueError as err:
print(err)
return
try:
average = sum(number_list)/len(number_list)
except TypeError:
print('List should contain numbers')
return
except NameError:
print('List should contain numbers')
return
return average
The code works fine for:
print(calculate_average([1, 2, 3]))
print(calculate_average([1, 2, 'a']))
But when I use:
print(calculate_average([1, 2, a]))
I have the following error that was supposed to be captured by except
:
NameError: name 'a' is not defined
Can you please help me with understanding the issue? (I use Spyder)
The NameError
on a
is raised in the calling scope, not when you attempt to use number_list
. You would need to catch it there:
try:
print(calculate_average([1, 2, a]))
except NameError:
print("Variable not defined")
However, you shouldn't be catching NameError
s at all. When they arise in testing, you should figure out what undefined name you are trying to use, and make sure it is defined. Like most exceptions, this isn't intended for flow control or dealing with easily fixed problems at runtime.
Rather than littering your code with run-time type check, consider using type hints and static typecheckers like mypy
to catch code that would produce a TypeError
at runtime.
# list[int] might be too restrictive, but this is a simplified
# example
def calculate_average(number_list: list[int]):
average = sum(number_list)/len(number_list)
return average
They only error left here that mypy
wouldn't catch is the attempt to divide by zero if you pass an empty list. That you can check for and handle. You can raise a ValueError
, or just decide that the average of an empty list is 0 by definition.
def calculate_average(number_list: list[int]):
if not number_list:
# raise ValueError("Cannot average an empty list")
return 0
return sum(number_list)/len(number_list)
This is preferable to
try:
return sum(number_list)/len(number_list)
except ZeroDivisionError:
...
because it anticipates the problem before you go to the trouble of calling sum
and len
.