Errors#

A lot of the time you spend programming will be in the process of “debugging” your code. This is the process of removing mistakes (“bugs”) from the code so that it produces the correct answer. There are three categories of mistake, usually called errors.

Syntax errors#

Syntax errors are when you write code that Python simply cannot understand, because it doesn’t follow the rules of Python code. For example, the following code cell produces an SyntaxError, because Python expects something to follow the plus sign.

a = 1 +
  Input In [1]
    a = 1 +
           ^
SyntaxError: invalid syntax

Syntax errors are detected before Python tries to run your code. You will probably need to fix a lot of syntax errors as you learn to code in Python, but thankfully Python is quite good at telling you where the mistake is. In the example above, Python indicates with an arrow that there is a problem after the + sign. This arrow is often useful and usually points to the correct place, but you should be aware that sometimes Python will be confused and tell you that the error is later than it is (very often on the next line).

Runtime Errors#

Runtime errors occur when the code seems to be valid, but for some reason Python cannot do what you told it to. In the following code cell, we intend to compute \(a(3 + 12.4)\) for \(a = 4\).

a = 4
a(3 + 12.4)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Input In [2], in <cell line: 2>()
      1 a = 4
----> 2 a(3 + 12.4)

TypeError: 'int' object is not callable

This produces an error because Python thinks we are trying to call a function called a with the argument \(3 + 12.4\), but a simply contains a number! It’s not a syntax error, because a(3 + 12.4) is perfectly valid Python code if a happens to be the name of a function when that line is reached. Another example is the following, where we end up accidentally dividing by 0.

a = 3
b = 4 / (3 - a)
---------------------------------------------------------------------------
ZeroDivisionError                         Traceback (most recent call last)
Input In [3], in <cell line: 2>()
      1 a = 3
----> 2 b = 4 / (3 - a)

ZeroDivisionError: division by zero

Here’s another extremely common source of runtime errors: try restarting the kernel through the Kernel menu and then running the following cell.

np.sin(0)
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
Input In [4], in <cell line: 1>()
----> 1 np.sin(0)

NameError: name 'np' is not defined

A NameError will probably be produced telling you that np is not defined; this is because it hasn’t been imported yet, and the fix is to ensure that any module you use is imported before it is used. You will likely encounter this error a lot!

Semantic errors#

Semantic errors occur when code runs, but does not produce the results that you expect. This is usually the most annoying sort of error to debug, since Python cannot tell you where the mistake is. When trying to fix semantic errors, its important to remember that Python very rarely makes mistakes; the problem is almost always that you are not telling it to do what you think you are!

For example, we can obtain the number of digits of a number \(n\) by rounding down the base-10 logarithm of \(n\) and adding 1. Numpy provides a log function, and a floor function for rounding down, so we might try the following.

import numpy as np
n = 312
number_of_digits = np.floor(np.log(n)) + 1
number_of_digits
6.0

The output of the last cell doesn’t seem right! What went wrong? If we check the list of mathematical functions provided by numpy again, we notice that log computes the natural logarithm rather than the base-10 logarithm. The function we need is called log10.

n = 312
number_of_digits = np.floor(np.log10(n)) + 1
number_of_digits
3.0

One of your best methods of preventing semantics errors is to document your code. Documenting your code means writing a description of what it is supposed to achieve. It goes hand-in-hand with adding comments to your code. In Jupyter Notebooks, we usually write about the code and/or provide an overview of how it works inside text cells like this one, and give more detailed discussion in comments inside the code. This idea is demonstated in the next few cells, which contain a semantic error:

In the following cell, we create a turtle on screen, and have it draw an equilateral triangle of side-length 100 units.

from mobilechelonian import Turtle
terry = Turtle()   # create a turtle, and call him terry

terry.forward(100)  # move forward 100 steps to draw one side of the triangle
# corner angle of an equilateral triangle is 60 degrees, so turn 60 degrees to the left
terry.left(60)
terry.forward(100)
terry.home()       # return to start to draw last side of triangle

Writing down what the code is supposed to achieve is good practice; it clarifies the code for both you and any other reader of your Notebook. In the example above, a reader can clearly see that the code does not do the right thing, since the triangle is not equilateral. Without explanations, the reader could waste a lot of time trying to work out what the code is intended to do (and in this case would never even know that it is incorrect!)