Python's Not (Just) For Unicorns

An interactive introduction to programming in Python, for human beings and whoever else

Chapter 13

Intro to debugging

Now that we know about conditionals and prompting the user for information, we can start to party a little bit harder. Last time we ended up with this piece of art, that tells you what kind of animal you’re listening to:

sound = input("What sound does the animal make?")

if sound == 'meow meow':
  print("It's a cat!")
elif sound == 'woof woof':
  print("It's a dog!")
else:
  print("I don't know what animal it is.")

Let’s try to do the same sort of thing, but with numbers this time.

  • We’ll ask the user for a number
  • Did they guess 7? They’re right!
  • If their guess was less than 7, print that they were too low
  • If their guess was greater than 7, print that they were too high.

I wrote the code out for us already, give it a run and see if it works (Hint: it won’t!). Type in 5 as your guess.

guess = input("Please guess a number between 1 and 10")

if guess == 7:
  print("You got it!")
elif guess < 7: 
  print("You guessed too low")
elif guess > 7:
  print("You guessed too high")

Oh no! An error! Our code has a bug! Let’s start crying, immediately! We can’t handle it!

But we must handle it. We must… debug!

Once our eyes are finally dry, let’s take a moment to read the error message. This is an important step every time you see an error! The message is TypeError: unorderable types: 'str' < int(), but to make it easier we can look at it piece by piece.

  • TypeError is probably something about data types, like strings and floats and lists and stuff.
  • unorderable types is about.. I dunno, we can’t put something in order?
  • 'str' < int() seems like the error we got a million years ago about adding an string and an integer, except this one seems to be about comparing a string and an integer

If we go up a little further in the long long lines of error message, Python actually shows us the line that gives the error:

Traceback (most recent call last):
  module __main__2 line 53
    self.run()
  module __main__2 line 47
    self.write(traceback.format_exc())
  module <module> line 5
    elif guess < 7: 
TypeError: unorderable types: 'str' < int()

See? The second to last line shows us our maybe-bad code. Looks like it’s elif guess < 7: — How useful!

…but also how weird, because our error seems to be “you’re can’t compare a string and an integer.” But how can that make sense? I’m pretty sure we’re comparing two integers, right?

When we asked for a number, we typed in 5. That’s an integer, obviously. And we saved that into a variable, so guess must be an integer, too. But when we ask if guess < 7, Python starts yelling that guess is a string (because 7 is definitely an integer!).

I refuse to belive that guess is a string! I totally super promise that guess is an integer! Right? Right?? To confirm that guess is an integer and Python has gone crazy, let’s print out the data type of guess right after we save it. If it’s an integer, it should say int.

guess = input("Please guess a number between 1 and 10")

print(type(guess))

if guess == 7:
  print("You got it!")
elif guess < 7: 
  print("You guessed too low")
elif guess > 7:
  print("You guessed too high")

We still get an error, but see the very first output line? It’s when we printed out the data type of guess, and it says…. <class 'str'>. Wait, what? The guess variable is a string!

Here’s a secret: Every time you use input to get something from the user, no matter what is typed in, Python always saves it as a string! So instead of the number 5, which is what we think we typed, Python’s using "5" instead, which looks exactly the same but is a string, not an integer! So when we compare it with the number 7, Python freaks out.

Okay, now that we know our problem, let’s try to fix it.

To make the comparison without Python yelling at us, we need to convert our guess variable into a number. To do that, we’re going to use int — remember a million years ago when we used str(2) to convert the integer 2 into the string "2"? It’s just like that, but the total opposite.

See how we use it below.

guess = input("Please guess a number between 1 and 10")

if int(guess) == 7:
  print("You got it!")
elif int(guess) < 7: 
  print("You guessed too low")
elif int(guess) > 7:
  print("You guessed too high")

We could use float the same way, but since it doesn’t have decimals we’re okay with int.

But let’s be honest — it looks kind of silly converting guess again and again and again. We’re too lazy to type all that! Maybe instead we can convert it once and permanently change its data type?

To do that, we need one more change to our code — right after we read in the guess, we’re going to say “let’s convert guess into an integer, then save it right back on top of the old guess variable.” The old string version of guess is replaced with the new int version.

Run the changed code, then read the output.

guess = input("Please guess a number between 1 and 10")

guess = int(guess)

if guess == 7:
  print("You got it!")
elif guess < 7: 
  print("You guessed too low")
elif guess > 7:
  print("You guessed too high")

What an adventure!

It took a while, but we eventually tracked down and solved our problem. This process is called debugging, and it’s a very very important part of programming.

If you can write code that’s always perfect… you aren’t real. Sorry! For the rest of us, we’re going to have bugs and errors popping up all of the time, and figuring out how to fix them (without crying too much) is going to make our lives much much easier.

Let’s think about how we used debugging to solve this problem:

  • First, we read our error message closely. We didn’t understand all of it, but some of it made sense, and we’d seen similar error messages before.

  • Then, we looked at the line it said had the error. Your error isn’t always on the line Python says, but it’s a good start. It will usually help you understand the error message better and give you an idea of what to change.

  • Then, we printed things out to confirm what was happening. A lot of time when you’re coding, you think you know what’s happening, but you can’t always be sure unless you print and print and print. We thought our number was an integer, but to make sure we printed out its data type.

  • Then, we made a small change and ran our code again. We were lucky that it worked on the first try. If it didn’t, we’d need to go back to reading our error message and seeing what else we might have done wrong.

It takes a long time to remind yourself to slow down and check each one of these steps every time you run into an error (especially the print part!). It’s common to randomly change code, hoping it will work, instead of taking a few seconds to calm down and focus your energy. I promise that reading error messages, trying to understand them, and debugging step-by-step is going to be super helpful (and super common!) in our coding journey.

But hey, even if it’s impossible, we can hope we’ll never see bugs again, right?

Chapter summary

We practiced tracking down and solving errors, and in the process learned that using input to get data from the user always saves as a string.