Debugging in Python

Jake Son


A major and potentially frustrating difference between Python and Snap! is that Python is a text-based language (while Snap! Is blocks-based), so there are more syntactical specifics to be aware of when working with Python. Much wisdom can be found here in CS61A’s Debugging guide. It contains information about error messages you will receive in Python, the types of errors you will encounter and debugging techniques. This guide is a short collection of errors, important functions, and debugging tools for new Python programmers.

This guide assumes you:

Outline

  1. Printing and Returning
  2. Working with Lists
  3. Working with Dictionaries
  4. Conclusion


1. Printing and Returning

Python’s print function is equivalent to Snap!’s say block. Neither print nor say return a value. They produce a side effect.

This Snap! Code doesn’t report (return) anything. It says (prints) a value.

Python’s return function is equivalent to Snap!’s report block. Return and report give values, and can be used to define variables. Let’s look at an example:

>>> def doubler(num):

. . .             return num * 2

>>> def print_doubler(num):

. . .             print(num * 2)

>>> a = doubler(4)

>>> a

8

Here, we see that when a is set to doubler(4), doubler(4) returns 8, so a becomes 8.

>>> b = print_doubler(a)

16

>>> b                 #no return value

>>> print(b)

None

Setting b to print_doubler(a) does not set b to 16, it just prints 16 in the terminal. The function print_doubler doesn’t return anything, so b returns None.

Like report, return outputs a value and ends the entire function call. A common tactic for debugging is to include print statements, which don’t affect the ‘flow’ of a program, specifically to find errors and then remove them when you’re finished.

2. Working with Lists

You’ve worked with lists in Snap!, and Python lists are similar. They are mutable iterables and have built-in methods (functions) that you should gain familiarity with.

Let’s work with the following list:

>>> weezer = [‘Rivers’, ‘Jason’, ‘Matt’, ‘Patrick’]

#may contain historical inaccuracies as the example progresses

Append, Extend

Append is a method that adds a single element to a list and doesn’t return a value when run.

>>> weezer.append(‘Brian’) #doesn’t return anything

>>> weezer

[‘Rivers’, ‘Jason’, ‘Matt’, ‘Patrick’, ‘Brian’]

>>> weezer.extend(‘Mikey’)

>>> weezer

['Rivers', ‘Jason’, 'Matt', 'Patrick', ‘Brian’, 'm', 'i', 'k', 'e', 'y']

That was strange. Extend takes in an iterable such as a list or string and adds each item to a list.

>>> weezer = ['Rivers', 'Brian', 'Matt', 'Patrick'] #fresh start

>>> weezer.extend([‘Mikey’, ‘Scott’])

>>> weezer

['Rivers', 'Brian', 'Matt', 'Patrick', 'Mikey', 'Scott']

Pop, Remove

Pop mutates a list by removing an item by index. If no argument is passed in, then pop removes the last item. Pop returns the removed value.

>>> scott = weezer.pop()

>>> scott

‘Scott’

>>> weezer.pop(2)

‘Matt’

The method remove alters a list by value, not index. It returns None.

>>> weezer.remove(‘Mikey’) #if there were multiple ‘Mikey’ values, remove would get rid of the first one

>>> weezer

['Rivers', 'Brian', 'Patrick']

        

Indexing

When working with lists, it may be easy to make mistakes with indexing. Remember that lists are zero-indexed, meaning that the position of a list starts at zero and ends (non-inclusively) at len(list). (Avoid naming your list ‘list’, as list is already a built-in type.)

>>> weezer[0:len(weezer)]

['Rivers', 'Brian', 'Patrick']

>>> weezer[:3] == weezer[0:3] #starting 0 is optional

True

Lists are mutable and can cause problems when a function such as append or pop is used incorrectly. While working with large programs, it may be helpful to make copies of lists. Copies can be made in two ways:

>>> weerez = weezer[:] #1

>>> zerwee = list(weezer) #2

>>> weerez == zerwee

True

Mutating a copy of a list does not mutate the original list. (i.e. zerwee.pop() doesn’t mutate weezer)

3. Working with Dictionaries

There’s no Snap! Equivalent to a Python dictionary. Dictionaries are iterables that contain keys and values. Here’s an example:

>>> grades = {'A': 4, 'A-': 3.7, 'B+': 3.3, 'B': 3, 'B-': 2.7, 'C+': 2.3, 'C': 2, 'C-': 1.7, 'D': 1, 'F': 0}

>>> letters, nums = grades.keys(), grades.values() #multiple assignment, a.k.a. creating multiple variables in one line

>>> letters

dict_keys(['A', 'A-', 'B+', 'B', 'B-', 'C+', 'C', 'C-', 'D', 'F'])

>>> nums

dict_values([4, 3.7, 3.3, 3, 2.7, 2.3, 2, 1.7, 1, 0])

Dictionary Iteration

To loop through a dictionary, you can try a few things:

Loop through keys

Loop through values

Loop through keys and values

for key in dictionary:

for key in dictionary.keys() also works

for value in dictionary.values():

for key, value in dictionary.items():

for key, value in zip(dictionary.keys(), dictionary.values()) also works I guess

>>> for letter, point in grades.items():

. . .            print(letter, point)

A 4

A- 3.7

B+ 3.3

B 3

B- 2.7

C+ 2.3

C 2

C- 1.7

D 1

F 0

Dictionary Mutability

Like lists, dictionaries are mutable.

>>> grades[‘A+’] = 4.25 #add A+ to the dictionary

>>> grades

{'A': 4, 'A-': 3.7, 'B+': 3.3, 'B': 3, 'B-': 2.7, 'C+': 2.3, 'C': 2, 'C-': 1.7, 'D': 1, 'F': 0, 1: [3], 'A+': 4.25}

>>> grades[‘A+’] = 4 #doesn’t add an extra key/value pair, it just replaces the current value associated with A+

Note that a key can’t have multiple values, but a value can belong to multiple keys. (A and A+ both have values of 4)

4. Conclusion

Programming is problem-solving, so the logic of Snap! applies to Python and vice-versa. When you’re stuck on a coding challenge, consider writing out your code on paper, as this may help you work through your thought-process without relying on the quickness of a computer. This guide is certainly not inclusive of all your Python needs, but we hope it was a useful starting point. Further resources can be found here. Good luck!