Facing up to mistakes

INPUT · Slides

Look inside with print

01 / 07

Mistakes that do not error

When something errors, the traceback tells you where. The awkward case is when nothing errors and the answer is still wrong.

Python only did what it was told, so it has nothing to say. The value you think is there and the value that is there have parted company — finding that spot is the whole of bug hunting.

The way you find it is very plain. Show what a variable holds, somewhere suspicious. That is all there is to it.

02 / 07

Show the value partway through

You have code adding things up and the answer feels wrong. When that happens, put one print inside the sum.

Now you can see total on every pass, so you can tell where it starts going wrong.

scores = [80, 60, 70]total = 0for score in scores:    total += score    print(total)print("answer", total)

Result

80
140
210
answer 210

03 / 07

Put a label on it

Set down several print calls and you lose track of which number is which. Label what you are showing.

Either list things with commas or use an f-string. The variable name makes the least confusing label, so use that.

score = 80count = 3print("score:", score)print(f"count: {count}")

Result

score: 80
count: 3

04 / 07

Guess before you look

This is the part that matters. print is not simply a way of showing things — it is a way of testing what you believe.

Before you look, make a guess: "it should be 3 by now". If it is, everything up to there is sound. If it is not, the cause lies further back.

In the example below three fruits are only counted twice. You can see that the range on range is wrong.

fruits = ["apple", "orange", "grape"]count = 0for i in range(2):    count += 1    print("i:", i, "count:", count)

Result

i: 0 count: 1
i: 1 count: 2

05 / 07

Put one at the way in and the way out

When a function is involved, the first thing to settle is whether what you handed in or what came back is the wrong one.

Show the arguments at the top of the function, and show what you are returning just before the return. That separates "what went in was bad" from "the sum inside is bad".

def with_tax(price):    print("got:", price)    answer = price + price // 10    print("returning:", answer)    return answerprint(with_tax(200))

Result

got: 200
returning: 220
220

06 / 07

Take them out once you have found it

A print you added to investigate should come out once you know the cause. Left in, it gets tangled up with the output you actually wanted to show.

You may be wondering whether something this primitive is really allowed. It is. Professional programmers do it every day, because it is the easiest thing to reach for and the hardest to get wrong.

The tools get grander, and the job stays the same. Hold the value you expect against the value you have. Nothing more.

07 / 07

Halve the range each time

The hunt goes like this.

  • from the wrong output, narrow down the stretch you suspect
  • put a print around the middle of it
  • if it matches your guess the cause is after that point, and if not it is before
  • halve the stretch again and again until one line is left

Because you are halving, a hundred lines takes about seven goes. Far quicker than staring and hoping.

The next group turns to getting ready for mistakes: rather than hunting them after the fact, you decide for yourself how things behave when they happen.