Skip to content

✢ Problems

Learning Objectives

At the end of this sub-unit, students should

  • understand sources of problems.

Preliminary

Python program must follow a very strict syntax. Deviations from this very strict syntax will introduce our first obstacle in writing program called syntax error. Here is a simple example.

1
2
3
4
5
>>> x = 1O
File "<console>", line 1
  x = 1O
      ^
SyntaxError: invalid decimal literal

What is the problem here? Although it looks similar, the character after 1 is actually an uppercase O and not the numeric 0. This is a simple error and Python has kindly told us the approximate position where the error is located.

Some errors are costlier than others. For instance, in 1962, the Mariner 1 was destroyed by the Range Safety Officer a mere 293 seconds after launch. This is because Mariner 1 was veered off course. What was the cause?

Additionally, the Mariner 1 Post Flight Review Board determined that the omission of a superscript overbar when translating the handwritten equations into the coded computer instructions allowed transmission of incorrect guidance signals (basically instantaneous values instead of smoothed values) to the spacecraft when the airborne beacon was inoperative.

So partly, the cause was using the wrong variable. These are mainly typographical error. The syntax error earlier can be caught by Python but the latter one cannot be caught by Python. This sub-unit is a study of the kinds of problems that we may encounter in Python.

Some problems like "logic problems" are harder to spot. Recently there were quite a number of leap year errors regardless of how many textbooks actually has that as a question. Even this note has the leap year problem. Some of the recorded problems are the following.

What is Bugs?

Historically, a bug is caused by a real insect. In 1947, Grace Murray Hopper was working on the Harvard University Mark II Aiken Relay Calculator. In the olden days, computers were the size of a wardrobe.

Aiken

The interesting thing happened on 9 September 1947. The machine was experiencing unknown problems. Upon investigation, it was found that there was a moth trapped between the points of Relay #70 in Panel F.

Bugs

There is even the moth attached with the note saying:

First actual case of bug being found.

So from this point onwards, we call a programming problem as bugs. The act of removing errors (i.e., removing bugs) is called debugging. Note that after debugging, the program is not necessarily error free. It just means that whatever errors remain are harder to find. This is especially true for large applications.

Quote

"If debugging is the process of removing software bugs, then programming must be the process of putting them in."

Edsger Dijkstra

Our mantra for this sub-unit is the follong.

Mantra

Humans make mistakes. We are only humans. Therefore, we will make mistakes.

So the idea is how do we mitigate the damage, how do we track the cause, and how can we fix the bug?

Common Types of Errors

Omitting Return Statement

This may be a problem if we expect the function to return a value. By omitting return statement, the function will run but the return value will be None. In cases where we expect a printed output, this behavior might be okay. However, in most cases, we expect a return value for a function.

Incorrect Code

def square(x):
  x * x

Correct Code

def square(x):
  return x * x

Incompatible Types

Now we consider the correct definition of square above. What problems can occur? It is possible that we actually use incompatible types during the function call.

Incorrect Usage

>>> square('2') # cannot use string
TypeError

Correct Usage

>>> square(2)
4

Another possibility is that we forgot to use parentheses to invoke the function. Since we cannot add a function with an integer, we get the following problem.

Incorrect Usage

>>> 5 + square
TypeError

Correct Usage

>>> 5 + square(2)
9

Incorrect Number of Arguments

Still yet another potential problem is to call a function with the wrong number of arguments. There are some special functions that allows an arbitrary number of arguments. In the future, we will also learn about functions that require named arguments. But for now, for functions we created, the functions need to be invoked with the correct number of arguments.

Incorrect Usage

>>> square(3, 5) # only 1 argument
TypeError

Correct Usage

>>> square(5)
25

Undeclared Variables

We may also accidentally use undeclared variables. Variable is used if it does not appear on lhs of an assignment lhs = rhs. So there are many possible context for this.

Additionally, if it appears in lhs, it must appear on its own. In the future, we will learn about a mutable sequence such as list where the following assignment is allowed.

lst[idx] = val

Here, both lst and idx are still considered to be used.

The use of underclared variables can happen in two different context which will give us different error messages.

Undeclared Variables

1
2
3
4
def abs():
  if x < 0:
    x = -x
  return x
1
2
3
4
5
>>> x = x - 2
NameError: name 'x' is not defined
>>> x = -2
>>> abs()
UnboundLocalError

Syntax Errors

There are quite a number of possible syntax errors. We have shown one before and we will show a few others here. Note that these are not a complete list.

Incorrect Indentation

1
2
3
def proc(x):
  do_stuff()
    more_stuff()
IndentationError

Value as Parameters

def proc(100):
  do_stuff()
SyntaxError

Missing Parentheses

1
2
3
def proc(x)
  do_stuff()
  more_stuff()
SyntaxError

Logic Error

Now we go into the most complex kinds of errors that may go undetected. The main difficulty is that these are not syntax errors so the errors only appear if the code is executed.

Divide by Zero

1
2
3
4
5
6
7
>>> x, y = 3, 0
>>> x // y
ZeroDivisionError: integer division or modulo by zero
>>> x % y
ZeroDivisionError: integer modulo by zero
>>> x / y
ZeroDivisionError: division by zero

Infinite Loop: Bad Inputs

1
2
3
4
5
6
def factorial(n):
  res, val = 1, 1
  while val != n:
    res = res * val
    val = val + 1
  return res
>>> factorial(2.1)  # infinite loop!

Infinite Loop: Incorrect Code

1
2
3
4
5
6
def factorial(n):
  res, val = 1, 1
  while val != n:
    res = res * val
    # forgot to update val
  return res
>>> factorial(2)  # infinite loop!

Incorrect Result

1
2
3
4
5
6
def factorial(n):
  res, val = 0, 1
  while val <= n:
    res = res * val
    val = val + 1
  return res
>>> factorial(10)  # always zero
0