3 / 0
ZeroDivisionError: division by zero
Programming for Data Science
An error is anything that causes a program to stop working.
Anything in the realm of writing and running code, that is — we are not considering here such things as power and hardware failures.
Errors may be classified into a variety of broad types:
Syntactic errors, AKA parsing errors, which are caused by not following Python’s rules for writing code. For example, you start a variable name with a number.
Logical errors, such as dividing by 0 or indexing into a list for an element that doesn’t exist.
User errors, such as when a user inputs the wrong data type for an operation.
System errors, such as when a database connection is down or a write operation is blocked by incorrectly set file permissions.
Dependency errors, such as when a module expects a version of another library that is not installed.
And so on.
Some of errors are in your control and some are not.
You can control how you write and design your code.
And you, or your team, can often control what software is installed and running on the system running your code.
But you can’t control how users or external systems will behave.
In cases where errors are in your control, you should update your code to eliminate them.
In cases where they are out of your control, you should anticipate and handle them so that your code may continue to execute.
The official Python documentation defines an exception as follows:
Errors detected during execution are called exceptions.
This excludes syntactic errors and dependency errors and focuces on problems that arise when code is running and data is being processed.
However, this definition does not make a distinction between what’s in an out of your control.
In practice, errors that are out of your control require that you make exceptions for them.
Anticipating and working around these errors is called exception handling.
To handle an exception means two things:
To provide a way of catching it
To provide a response when it is caught
Python provides tools for identifying errors and handling them when they arise.
Exceptions are represented as a collection of Python object that classify errors.
For example, a ValueError
is one in which an incorrect value is used for a given operation.
Exception Handlers are control structures and functions to deal with exceptions. These include:
try
/except
raise
assert
Python comes with a number of predefined exceptions, in addition to ValueErrror
.
Modules introduce new ones, too. For example, a database connection module might include exceptions for a broken connection or for a SQL constraint violation.
Exceptions are used in a raise
statement by the authors of programs when a possible error is anticipated.
Here are some common examples.
ZeroDivisionError
3 / 0
ZeroDivisionError: division by zero
SyntaxError
An if-statement missing colon at end:
if x > 0
print("uh oh")
SyntaxError: expected ':' (2612676770.py, line 1)
NameError
This code references an undefined variable:
print(x)
NameError: name 'x' is not defined
IndexError
This loop goes beyond the length of the list:
= [0, 1, 2]
lst
for i in range(4):
print(lst[i])
0
1
2
IndexError: list index out of range
You can see that when Python encounters errors, it stops running and prints an error message detailing the error type and its location in code.
You should get used to reading these messages. Although they may be off-putting and suffer from TMI, they do point to the specific line in your code where the error was encountered.
try/except
Python provides try/except
blocks to handle exceptions in uyour code.
These blocks work as follows:
First, the try
block will contain a statement.
Then, if the statement inside the block fails, the flow switches to the except
block.
The exception block will have code to handle the error, rather than halting the program.
The process is very similar to if/then
:
Multiple except
statements may be given, to handle specific exceptions.
Below, we give a catch-all except
for any kind of exception.
print(a)
NameError: name 'a' is not defined
Now let’s try referencing a variable that doesn’t exist with a try/except
block to handle the exception.
try:
print(a)
except:
print("caught an exception")
caught an exception
We could also do this:
try:
print(a)
except NameError as e:
print(e)
name 'a' is not defined
Here we print the message, but the program continues to run.
else
and finally
Exception handling blocks may also contain else
and finally
statements.
else
statements run if no errors are caught.
finally
statements are run no matter what happens. This is used in cases where variables or connections are set up in the process of testing and need to be deleted or closed.
Here is a an example of a function that contains a fully qualified exception handling block:
def divide(x, y):
try:
= x / y
result
except ZeroDivisionError:
print("Error: Can't divide by 0!")
else:
print("Result:", result)
finally:
print('This is always executed')
3, 2) divide(
Result: 1.5
This is always executed
3, 0) divide(
Error: Can't divide by 0!
This is always executed
Another way to catch errors is to test if computations turned out as expected.
We use assert
to verify if an expression is True
.
If an expression is True
, nothing happens.
If an expression is False
, Python raises an AssertionError
exception.
Assert statements have the following syntax:
assert Expression, Message
The message is optional. It is printed if the expression is False
.
For example, consider a program that is expecting three arguments to be passed from the command line.
Assume the variable num_args
represents the number of arguments.
= 3
num_args assert num_args == 3, "number of arguments must be 3!"
Here, the assert
evaluates to True
, and things proceed normally without exception.
If we change num_args = 4
this will throw an AssertionError
with the provided message.
The program then stops.
= 4
num_args assert num_args == 3, "number of arguments must be 3!"
AssertionError: number of arguments must be 3!
If the assert
is not given a message, it throws AssertionError:
without a message.
= 4
num_args assert num_args == 3
AssertionError:
You can mix assert
statements withtry
/except
blocks.
try:
assert num_args == 3
except:
print("Got an error")
Got an error
Note that the except
message will append the assert
message in this case.
try:
assert num_args == 3, "Assert: Number of arguments must be 3!"
except AssertionError as e:
print('Except:', e)
Except: Assert: Number of arguments must be 3!
raise
Exceptions can be raised, too.
When using raise
, you must pass it an exception type function.
The argument of the function is a message.
In this example, we want to stop the program if the code encounters a ZeroDivisionError
.
= 0 # Some value given by a user
x
if x == 0:
raise ZeroDivisionError("Hey, you can't divide by zero!")
ZeroDivisionError: Hey, you can't divide by zero!
Exceptions are errors that arise in a running program that are expected to happen.
We handle exceptions with try
and except
statements.
raise
and assert
statements are ways to make your code responsive to errors that may arise.
We sometimes use these to actually stop program flow so that users of our code can catch them and handle them.