Python: debugging, more loops
List operations
We ended last time looking at a couple of functions that use for-loops:
def sum(lst: list) -> int: """returns the sum of a list of numbers""" running_sum = 0 for n in lst: running_sum = running_sum + n return running_sum # courses_in_dept(["CSCI0111"], "CSCI") == ["CSCI0111"] def courses_in_dept(courses: list, dept: str) -> list: """returns a list of courses whose names start with dept""" found_courses = [] for course in courses: if course.startswith(dept): found_courses.append(course) return found_courses
In Pyret, we wrote list processing functions using both cases
expressions
(which, as we’ve seen,, we will replace with for-loops when we write Python
code) and the built-in list operations such as filter
, map
, etc. Python also
has built-in list operations; for example, the above loop could be re-written as
a filter
expression:
def courses_in_dept(courses: list, dept: str) -> list: """returns a list of courses whose names start with dept""" return list(filter(lambda course: course.startswith(dept), courses))
Things to notice here:
- Python has lambda-expressions, just like Pyret does. The syntax is slightly different, but they are doing the same thing.
- We need to call
list
on the result offilter
–we won’t talk about why in this course.
Debugging
Let’s write one more function that uses a loop:
def longest_string(strings: list) -> str: """return the longest of a list of strings""" longest = "" for s in strings: if len(s) >= longest: longest = s return longest
Let’s take a look at how this function works:
> longest_string(["cat", "or", "dog"]) "dog"
Imagine we are confused about why the function is returning "dog"
instead of
"cat"
. In order to figure it out, we need to understand how the function
executes. We could simulate its execution on paper, on a whiteboard, or in our
heads. Or, we could ask the computer to help us!
Print statements
One way to debug a function like this is to insert print statements:
def longest_string(strings: list) -> str: """return the longest of a list of strings""" longest = "" for s in strings: print(s) if len(s) >= longest: longest = s return longest
If we execute the function now, we see something like:
> longest_string(["cat", "or", "dog"]) cat or dog "dog"
We could insert another print statement:
def longest_string(strings: list) -> str: """return the longest of a list of strings""" longest = "" for s in strings: print(s) if len(s) >= longest: print(s) longest = s return longest
> longest_string(["cat", "or", "dog"]) cat cat or dog dog "dog"
We could even add some formatting:
def longest_string(strings: list) -> str: """return the longest of a list of strings""" longest = "" for s in strings: print("Looking at " + s) if len(s) >= longest: print(s + " is longer than " + longest) longest = s return longest
> longest_string(["cat", "or", "dog"]) Looking at cat cat is longer than Looking at or Looking at dog dog is longer than cat "dog"
This should give us enough information to diagnose the problem.
Debugging by stepping through the program
Another option is to use the step-through debugger. We can add to our file:
print(longest_string(["cat", "or", "dog"]))
The lecture capture demonstrates:
- starting the step-through debugger
- seeing how variable values change
- using the debugger console
See the lecture capture for details on how the step-through debugger works.
What are the pros and cons of print statements vs. the step-through debugger?
Another loop example
Let’s say we want to write a function that takes a list of strings and counts the total number of “z”’s that appear. How could we do this?
def zs_in_list(lst: list) -> int: count = 0 for s in lst: print(s) for c in s: print(c) if c == 'z': count = count + 1 return count