In this module you will learn how to step through your code line-by-line using a debugger. We'll also learn about another way to 'repeat' blocks of code using the for
loop. Additionally, we'll learn how we can handle more complex situations like iterating over 2 or 3-dimensional data using nested loop structures. And finally we'll discuss how to make our programs more robust using simple data validation.
Sooo... you've finished writing your new program π! All done, nothing else to do here, amirite? But wait! You have to test your program first to see if it works. When you test your program, assuming that it runs (there are no syntax issues), you'll inevitably encounter errors: runtime and logical errors. These program defects are called bugs π·πππ.
Debugging is the process of finding and resolving problems or errors in a program; this can be done by adding code to the program to aid in debugging (such as printing out values of variables) or by using a tool that facilitates the debugging process.
Debugging can be as simple as using print
statements to trace the execution of your code. This is a legitimate and very useful debugging technique (though it may result in many extraneous prints... and it may be trickier working through larger programs with just print statements).
If you choose to use a tool, you'll be using a debugger. A debugger is an application (either standalone or part of a text editor or integrated development environment) that aids in the finding and fixing of runtime errors and logical errors. Debuggers can be graphical, or they can be run through a commandline interface.
A debugger allows you to:
This is done by:
We haven't defined our own functions yet, so for now, using step over is adequate to advance to the next line of the program.
PyCharm has a built-in debugger. You can set break points by clicking a line number... and you can execute your program through the debugger by going to Run → Debug. This will bring up the debug panel where you can choose to stop execution of the program. Note that β οΈ you must set breakpoints prior to debugging, otherwise you will not have the opportunity to pause the program.
Check out the video ππΉ for a quick demo of debugging with print statements and with PyCharm's built-in debugger.
The while
loop structure that we learned about in Module 4 is especially useful when iterating an unknown number of times. However, when you want to write code that iterates a fixed number of times it's usually more convenient to use a "count controlled loop." In Python this kind of loop is called a for
loop.
A for
loop can iterate over any fixed set of items. Those items
can be as simple as a series of numbers, or more complex like a series of
strings or other data types. A for
loop has the following form:
for VARIABLE in ITEMS: STATEMENTS
In plain English this could be read as "for each VARIABLE that exists in ITEMS execute STATEMENTS." In this example the words in UPPERCASE will be replaced by the data you're working with. You can create a list of items by placing values separated by commas in between an opening and closing square brace like so: [ VALUE_1, VALUE_2, ..., VALUE_N ]
. The code in STATEMENTS is the body of the for
loop. The code in the body is going to be run once for each item in the list. In addition, each item in the list is temporarily stored in the specified VARIABLE for one iteration (execution) of the body.
Let's take a look at a concrete example to make it more clear:
Sample Program: The following program will iterate over the list of numbers provided and print each number one time. Try changing the values to experiment with it.
You must have at least one statement in your for loop, but you can have as many additional statements as you want. You can also use accumulators (like you did with while loops) or other variables from outside of your for loop as well.
Sample Program: The following program illustrates multiple statements with an accumulator.
The for
loops in Python can iterate over any type of data making it possible to use with strings, floats, characters, etc. In addition we can nest other programming structures inside of a for loop, so we can include statements like if, elif, else statements inside of a for loop. With each layer of nesting, we have to make sure our indention amount is equivalent to the level of nesting that we want in our program.
range()
functionIn this module, the first program we showed you looped through the set of integers from 10
to 50
skipping 10 each time. But what if we want to iterate over a much larger set of numbers (say, the numbers 1 through 100)? We could spend our time writing out every single number we want to iterate over inside of a list, but that would be a very inefficient use of our time. Instead we can use a new function called range
to generate a series of numbers for us.
The simplest form of the range() function is to pass it one parameter. For example:
range(10)
will return the series of numbers:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
So we see that range(N)
returns the set of numbers from
0
inclusively (including that number) to N
exclusively (excluding that number).
We now have a quick and easy way to get a large list of numbers:
Sample program: The program below iterates over a large set of numbers and computes a running total. Try these extensions to the program once you understand the basics:
range()
with a starting pointYou may not always want to start counting at 0
. Let's take the following situation for example. Suppose you want to ask the user what number to start and end? You could write code like this:
total = 0 start = int(input("Enter starting number")) end = int(input("Enter ending number")) for i in range(end + 1): # +1 to make it inclusive if i < start: # ignore values less than the starting point continue total += i print("Sum from", start, "to", end, "inclusively:", total)
The code works, but it's not very pleasant to read and there is a better way. The range()
function is pretty flexible
and has some extra functionality built-in to it that we can take advantage of. It
has what we call "optional" parameters. We can pass the range()
function both a starting and a stopping value in the form range(START_VALUE, END_VALUE)
.
We can simplify our code from above to incorporate these values and get rid of the if statement:
total = 0 start = int(input("Enter starting number")) end = int(input("Enter ending number")) for i in range(start, end + 1): # +1 to make it inclusive total += i print("Sum from", start, "to", end, "inclusively:", total)
range()
with a step sizeIf we want to find all the multiples of 5 from 1 to 50, we could have the following code:
for i in range(1,50+1): if i % 5 == 0: print(i, "is a multiple of 5")
As we said before, the range()
is very flexible, it actually has a way for us to use a step size in how much i
will be incremented with each iteration. For that, we include a 3rd parameter so the syntax becomes: range(START_VALUE, STOP_VALUE, STEP_SIZE)
. In that case, our above code can be simplified to:
for i in range(5, 50+1, 5): print(i, "is a multiple of 5")
So far we have show you how to count from a starting point and approaching an ending point and we have seen that by default the step value for each iteration is 1, but that value can be modified. Let's say that we want to write a countdown timer for a rocket launch system. We want that count to start at 10 and count down to 0. To do that, we can just set the step value to a negative value, so that it is subtracting instead of adding with each iteration!
Sample Program: counts backwards using range()
Starting number: 5 Ending number: 15 Even or Odd?: even 6 8 10 12 14Click the "Run" button to check your work, and click here to download the solution.
Programming Challenge: Write a program that asks the user to enter in a number of products (as an integer). Then prompt the user for that number of prices and compute the total cost of all products. Here's a sample running of your program
Enter a number of products: 3 Enter price for product # 1: 100 Enter price for product # 2: 200 Enter price for product # 3: 300 Total cost: 600
Click the "Run" button to check your work, and click here to download the solution.
A "nested" loop is a loop that has been placed inside of the body of another loop. The most common form of nested loop structures is probably a for
loop nested inside of another for
loop, but you can also have any set of nested combinations of structures such as: a for
loop inside of a while
loop, a while
loop inside of a for
loop, or a while
loop inside of a while
loop.
Nested for
loops can be extremely useful when dealing with
multi-dimensional data. A good example is any time you need to work with data
that has two or more pieces of information per entry. Let's take an image for
example. Every computer image we look at on a screen is made up a of a series of colored squares called "pixels" that are arranged as a grid. A pixel is the smallest representable part of an image. If we zoom in on a picture we
can see the individual pixels as block like structures. Here is an example:
In this example, we will use some pseudocode to demonstrate how we could iterate through each pixel in an image. Pseudocode is a design tool that we use as programmers to lay out the structural design of a program, without worrying about details of a particular language. Pseudocode tends to look a lot like Python code because Python code was significantly influenced by the simplicity of it.
# To process the entire image one pixel at a time, we first subdivide # the problem into processing each row of pixels, one row at a time. # process each row individually for every row in image_pixels: # Process an entire row, by iterating # over the row one pixel at a time for every pixel in row: # analyze and process a single pixel in the row. # after analyzing, the for loop will move to the next pixel # if no more pixels are in the row, it jumps back to the outer loop # When a row has been completed, # the program will move to the next row # if all the rows have been completed, the outer for loop will finish # At this point all the pixels will have been processed
Another example of using nested loops might be to ask the user for a number of years - we can then iterate over these years and then iteratve over the months in those years. Here's an example:
for year in range(2015, 2017): print ("Year: ", year) # now iterate over months in this year for month in range(1, 13): print ("Month #: ", month)
Programming Challenge: Write a program that asks a teacher for the number of students in his or her class. Next, ask the teacher how many assignments are given in this class. With this information prompt the user to enter in scores for each student and compute their average grade in the class. Here's a sample running of your program:
How many students in the class? 2 How many assignments in the class? 2 Student #1 Assignment #1: 100 Assignment #2: 90 Student #1 earned a 95 Student #2 Assignment #1: 90 Assignment #2: 80 Student #2 earned a 85.
Click the "Run" button below to test your program. You can download the solution to this problem by clicking here.
All of us have had programs crash on us or do things that we may not have expected. Often these are results of bugs in the program. When we design programs we want to write programs that are robust so that they crash as rarely as possible. Finagle's Law states: "Anything that can go wrong, willβat the worst possible moment." Our programs should be ready to handle these situations in a graceful way. That means that we should employ some amount of defensive programming when we write code. One way of writing code defensively is to check that the data we're receiving is within the range of valid values.
Take a look at this sample code:
numerator = input("Enter a numerator: ") denominator = input("Enter a denominator: ") result = numerator/denominator print(numerator, "divided by", denominator, "is:", result)
If the user enters a denominator of 0
, the program will crash:
Enter a numerator: 10 Enter a denominator: 0 Traceback (most recent call last): File "code01.py", line 3, inresult = numerator/denominator ZeroDivisionError: division by zero
Not only do these kinds of errors crash the program, but the error messages for the user can be very confusing and are full of extraneous information that they don't need to know about (but are helpful in debugging).
A better way to handle the situation is to check the data before you use it using the if statement to make sure it's within the proper range of values.
Sample Program: This program shows how you can perform simple data validation to check that invalid data is not being used in a calculation.
Or better yet, we can wrap the code that asks the user for a value inside of a while loop so that it keeps asking the user until we receive a valid input. Below are a couple ways this can be done. Compare the different ways and think about how each functions differently and what advantages each may have:
Until now all of our turtle graphics programs have been drawing in "real-time" (i.e. we have had to wait and watch as Python methodically followed our directions and rendered our shapes to the screen). This sub-module will introduce you to a series of additional functions which can be used to speed up this process and make it easier to draw more complicated shapes.
Sometimes you don't want to see the actual turtle cursor on the screen. You can remove the cursor easily by calling the hideturtle
function at the beginning of the program. Here's a quick program that shows this in action:
# make the turtle graphics module available import turtle # also make the random module available import random # set up our graphical canvas # width = 500, height = 500 turtle.setup(500, 500) # hide the turtle turtle.hideturtle() # draw a square for side in range(4): turtle.forward(100) turtle.right(90)
Note this will not speed up the turtle at all - this technique simply removes the small "triangle" associated with the turtle cursor.
You can adjust the drawing speed of the turtle by calling the speed
function. This function takes one integer as an argument - this integer describes how fast the turtle should draw shapes to the screen. 1 is the slowest speed for the turtle, 5 is moderate and 10 is fast. The fastest speed the turtle can draw at is speed 0. Here's an example of this in action:
# make the turtle graphics module available import turtle # also make the random module available import random # set up our graphical canvas # width = 500, height = 500 turtle.setup(500, 500) # hide the turtle turtle.hideturtle() # set the speed to the absolute fastest speed possible turtle.speed(0) # loop 100 times for j in range(100): # pick up the pen and move to a random position on the screen turtle.penup() turtle.goto(random.randint(-250, 250), random.randint(-250, 250)) turtle.pendown() # draw a square here for side in range(4): turtle.forward(100) turtle.right(90)
Sometimes turtle.speed(0)
isn't fast enough! In order to speed up the turtle even further you can use the following technique to more or less render your images instantly:
We can do this in turtle graphics by using the tracer
and update
functions. You can call tracer
to turn off drawing to the screen - the function takes one argument (an integer) which represents how often to draw to the screen. Sending this function the number 0 will essentially turn off drawing to the screen completely.
Next, you can draw all of your shapes as usual. When finished you can call update
to force the screen to draw itself. Here's a program that uses this technique:
# make the turtle graphics module available import turtle # also make the random module available import random # set up our graphical canvas # width = 500, height = 500 turtle.setup(500, 500) # hide the turtle turtle.hideturtle() # turn off drawing! nothing will show up when we draw from this point forward # the drawing will be stored in memory though turtle.tracer(0) # loop 100 times for j in range(100): # pick up the pen and move to a random position on the screen turtle.penup() turtle.goto(random.randint(-250, 250), random.randint(-250, 250)) turtle.pendown() # draw a square here for side in range(4): turtle.forward(100) turtle.right(90) # let turtle graphics draw what is in memory to the screen turtle.update()
Now that you've completed this module, please visit our NYU Classes site and take the corresponding quiz for this module. These quizzes are worth 5% of your total grade and are a great way to test your Python skills! You may also use the following scratch space to test out any code you want.