title

Tasks

  1. Complete reading this lesson, which involves the following:
    1. Overview of for Statement
    2. The range function.
    3. Problem Solving using for Statement
    4. Additional Operations
    5. Nested for Loops
    6. while vs. for loops
  2. Complete quiz 5 to verify your reading and understanding.
  3. Read and complete the activies at Zybook, Z7: For Loops.

Learning Outcomes

By the end of this lesson, you should be able to:

  1. Read Python loops constructed using for statements.
  2. Write Python for statements.
  3. Relate the concept of iterable objects in Python to for loops.
  4. Use the range function in for statements.
  5. Differentiate between solutions that require for or while statements.

Key Terms/Concepts

Introduction

Repetition of a task is not something new for humans. For example, when we are preparing a mixture for a cake, we need to repeat tasks like putting three teaspoons of sugar in the mixture. Let’s analyze this task on an abstract level.

In this lesson we discuss Python repetition structures. There are two types of repetition structures in Python, one is called for loops and other is while loops. They cause a group of statements to be executed repeatedly in a loop. Both for and while statements appear on most structural languages. However, Python has a distinct way of using for statements, which involves the use of iterable objects. In this lesson, we introduce the for statement.

The lesson starts by providing a holistic overview of the for statement and introducing the concept of iterable objects. The second section introduces the range function and presents different methods for generating number sequences. Then we present a step-by-step analysis of a problem that is solved using a for statement. These three sections provide what you essentially need to know about for statements. The rest of the module involves advanced operations with for statement. This involves the break, continue, nested for loops and for else.

The for Statement

6.2.1 Iterable Objects in Python

Objects in Python appear in different forms and types. One method to classify objects is to divide them into two groups: iterable objects and non-iterable objects.

An iterable object is any object that allows you to pick its member items (elements) one by one. Think of it as a box that has a collection of items, for example small balls. This box has a hole to the side which when you click on the 'iterate' button it starts throwing the balls out of the box one by one until no more balls remain in the box. Any box that has this 'iterate' button could be described as an iterable box (object), and any box that does not have this feature is called non-iterable.

The concept of iterability is not associated with what you are doing with each item. It is only associated with the fact that you can access the elements one by one. In the ball box example, it does not matter if the balls are being shot out of the box, if you insert your hand into the box and pick one ball at a time, or if you simply look through the hole and take a picture of each ball one at a time. In all of these examples, you have the capacity to access the elements one after the other. This is what iterability is.

Python is distinguished with the concept of iterability. This concept is absent from some languages like C. Other languages like Java and C++ provide some mechanisms for iterating through collection objects. However, this comes in no way closer to how Python supports this concept. In Python, iterability is more natural and native to the language basics.

Python has several built-in data types which are iterable. This includes strings, lists, tuples, sets and dictionaries. A user could also define their own objects and support them with the feature of being iterable. At this stage, the iterable data type which you are most familiar with is strings. Therefore, in the following section, when we present the for-statement, strings are used as the primary example.

In strings, the iterability works on the characters of a string. For example, the string ‘hello’ when iterated produces the letters h, e, l, l, o.  Now for the number 629, we cannot iterate through the digits 6, 2 and 9, because an integer is not an iterable object in Python.

Iterable objects work closely with for loops. We can say that in Python the primary use of a for loop statement is to iterate through iterable objects. This goes both ways, i.e. every for statement uses an iterable object and every iterable object could be used in a for statement.

6.2.2 Structure of for statement

The syntax of a Python for statement is defined below:


for <var> in <iterable>:
    <block of code>

You can read the above statement as the following: for every item in the iterable object, perform the following steps.

The for term is the keyword for the loop command. The <var> is any variable name of your choice. This variable is used to identify the element that you pick from the iterable object at each iteration. The in operator is a built-in Python operator that works only for iterable objects which checks if an item is found inside a given collection (iterable object). This operator is called the contains operator. Similar to the if and while statements, the for statement ends with a colon and the enclosed block is indented by the same level.

Let us take an example of iterating through a string using for loops.

Since the above example is too simple, let us look at another example to enforce your understanding of how for loops are used with strings.

The range Function

In the previous section, you saw how strings could be iterated using a for loop statement. However, it is not clear how a programmer would iterate through a sequence of numbers. Since integers and floats are non-iterable objects in Python, the for statement cannot be used directly with numbers. Python offers the function range to achieve this task.

In this section, you will learn how to use the range function within a for loop statement to iterate through numbers. This has several useful applications like counting and generating mathematical sequences.

Technical Note:

In Python 2, a range is a function. However, in Python 3 it was changed to a data type. You can verify this by trying:

x = range(0,5)

print(type(x))

However, for the purposes of this course, it is easier to still think of a range as a function. In future courses, when you get more understanding of object oriented principles, you may revisit this concept to understand what it means for Python to deal with a range as a data type. 

6.3.1 Generating Numbers from 0 to N

If a user is interested in generating a sequence of numbers between 0 to N, then the following syntax should be used:


for <var> in  range(<N>):
        <block of code>

The above syntax generates numbers from 0 up to, but not including, the number N. If a user is interested in 0 to N, inclusive both ends, then the for statement should be:


for <var> in  range(<N+1>):

At each iteration, <var> is updated with the next number in the sequence. At the start of the loop <var> is initialized to 0, and in the second iteration it gets the value of 1, and so forth.

As we mentioned earlier, it is up to the user to decide what to do with the numbers. The user could simply print the number or pass it to some expression or function for some processing.

Let us have a look at an example.

Example 6.3.A:

Write a Python function called print_nums(n). The function prints all numbers between 0 to n, but not including n. The function prints each three numbers on a separate line. In each line the numbers are separated with a tab character.

Below is the solution:


 # Program Name: Prog 6-03
 # Solution to Example 6.3.A
  
 def print_nums (n):
          for i in range(n):
            print("{}". format(i),end=’’)
             if (i +1) % 3 == 0:
                     print()
             else:
                     print(‘\t’, end=’’)
          return
  
 n = int(input("Enter value of n: "))
1 print_nums(n)

Code Listing 3: Solution to Example 6.3.A.

The lines of interest to this module are lines 5 to 10, so we analyze them:

Line 5 The for loop generates a sequence of numbers from 0 to n (exclusive). At each iteration, the variable i is used to refer to the generated number.
Line 6 Prints the generated number. Observe how the print statement ends with an empty string. Therefore, nothing is printed after each number, not even a newline.
Lines 7, 8 The conditional statement prints a new line whenever three numbers have been printed. Note how the term (i+1) is used to avoid the problem of printing a newline when the value of i is 0.
Lines 9, 10 The else statement prints a tab between numbers in the same line.

Running the above program produces output similar to the following:


Enter value of n: 8
0   1   2
3   4   5
6   7

Console 3: Output of Prog 6-03.

As you can see from the above example, using the range function within a for statement is quite simple. The formatting part of when to print a new line or tab is not directly relevant to the generation of numbers.

Technical Note:

When describing the range function, we have been using the term 'sequence of numbers'. This comes from the fact that the following code:

for i in range(0,4):

 print(i)

is equivalent to:

for i in 0,1,2,3:

 print(i)

However, there is a little trick here. The sequence 0,1,2,3 is not an actual sequence, because there is no data type in Python called sequence. It is a tuple. The concept of tuples will be covered in Lesson 12.

6.3.2 Generating Numbers from M to N

The basic range structure presented above always generates numbers starting from 0. But what if we would like to start from another number?

This could be achieved with a simple change to the for loop statement:


for <var> in  range(<M>,<N>):
        <block of code>

The above loop will generate a sequence of numbers between M (inclusive) up to N (exclusive). Again, the naming of the variables M and N is arbitrary, and we can select any other name of our choice.

This little change in using the range function grants us more flexibility, as we can include negative numbers in our sequences. Let us look at an example.

6.3.3 General Form of the range Function

After presenting the above two forms of the range function, it is time to present the general form:


for <var> in range(start,end,step):
        <block of code>

The main addition here is the step variable. The step determines how the numbers in the sequence should be spaced out. In the previous examples, the numbers were consecutive which means the step was 1. If you want to generate numbers in alternating fashion, then the step should be 2, and so forth.

Let us look into a scenario where using steps in the range function would produce more efficient code. Suppose you want to print all multiples of 5 which are between 0 and 100, inclusive both ends. Based on what we learned in 6.3.2, this could be done using the following:


for i in range(0,101):
    if i % 5 == 0:
        print(i)

The above loop will undergo 101 iterations. However, we can reduce the number of iterations by doing the following:


for i in range(0,101,5):
    print(i)

The above code will only undergo 21 iterations, which is about 20% of the previous number. Not only does this provide efficiency, but also simplicity as there is no need to introduce a conditional statement.

To ensure good understanding of the general form of the range function, you can practice with the following example:

By reaching this point in the lesson, you would have covered the basic principles of the for loop statement. What remains is only tips and hints in addition to some advanced operations. What you need at this stage is practice and more practice. We will offer in the following section a detailed example of a slightly more complex problem that requires the use of for loops.

Problem Solving Using for

In this section, we will present a detailed problem which requires the use of the for statement. We will take you through the thought process and show how a complicated problem could be simplified through gradual progress.

Here is the problem.

Example 6.4:

Assume you have the following series: \(\sum_{i=0}^{n} (a+i)x^i\)

The series can be expanded as: \(a + (a+1)x + (a+2)x^2+ \cdots +(a+n)x^n \)

Write a Python function called print_series(a,x,n). The function computes the sum and prints the series along with its sum using the above format.

6.4.1 First steps

At a first glance, the problem seems to be a bit complicated. Also, the series might not make sense to us, because we do not know how it is useful or in which application is it going to be used.

In programming, whenever we find a complicated problem we can always simplify it by breaking it into smaller achievable tasks. In other words, we should always be ready to produce a partial solution. The partial solution can be gradually upgraded to a full solution.

With regards to the usefulness of the series, as programmers doing the implementation, this does not really matter. We just need to make it work. Therefore, this should not be a distraction. Knowing the big picture is more relevant if you are doing a project that would require the application of software engineering principles. But we are not there yet, so let us move on.

We will start by writing the layout of the program. We can write the function definition along with the return value. In this specific example, there is no value to be returned. We can start coding by writing the following:


# Program Name: Prog 6-06
# Solution to Example 6.4
  
def print_series(a,x,n):
    # some code
    return

Code Listing 6: Solution to Example 6.4 (version 1).

At this stage, it would also be good to write simple main (driver) code to test the function. This will enable us to test the program as we progress. The main program contains simple input statements for the variables a, x and n along with a function call.

Here is the code:


# Program Name: Prog 6-06
# Solution to Example 6.4
  
def print_series(a,x,n):
    # some code
    return

a = int(input('Ente value of a: '))
x = int(input('Enter value of x: '))
n = int(input('Enter value of n: '))
print_series(a,x,n)  

Code Listing 7: Solution to Example 6.4 (version 2).

If we look at the problem carefully, we can identify that we would need to do three things:

  1. Generate the series
  2. Compute the sum of the series
  3. Print the series

 Let us focus on the first task.

6.4.2 Generating the Series

We recognize that there are three variables: a, x and n. All of these variables seem to be holding to their value throughout the series. However, we also see that there is a variable called i that progresses from 0 to n. This is a good candidate for a for loop statement. So, we can write the following:


def print_series(a,x,n):
    for i in range(n+1):
        #some code
    return

Code Listing 8: Solution to Example 6.4 (version 3).

At this stage, it should be clear to you why we used (n+1) instead of n in the range statement.

Next, we can plug-in the mathematical series.


def print_series(a,x,n):
    for i in range(n+1):
        (a + i)*(x**i)
    return


Code Listing 9: Solution to Example 6.4 (version 4).

The result of the above expression is not stored anywhere. Therefore, we can store it at a variable of our choice and print it for testing purposes. The code becomes:


def print_series(a,x,n):
    for i in range(n+1):
        series_term = (a + i)*(x**i)
        print(series_term)
    return

Code Listing 10: Solution to Example 6.4 (version 5).

If you test the above code with values of a = 5, x = 10, n = 6, you get:


Enter value of a: 5
Enter value of x: 10
Enter value of n: 6
5
60
700
8000
90000
1000000
11000000

Console 6: Output of Example 6.4 (Version 5).

If you verify the above using a calculator, you will find that the loop is generating the series successfully. Take few moments to celebrate this partial achievement!!

6.4.3 Computing Series sum

We have seen several examples in Lesson 6 and also recently in Example 6.3.C where we computed a running total using a loop. You can apply the same concept here. The code becomes:


def print_series(a,x,n):
    total = 0
    for i in range(n+1):
        series_term = (a + i)*(x**i)
        total += series_term
    return

Code Listing 11: Solution to Example 6.4 (version 6).

We can add a print statement for testing:


def print_series(a,x,n):
    total = 0
    for i in range(n+1):
        series_term = (a + i)*(x**i)
        total += series_term
        print(f'term = {}, total = {series_term,total}')
    return

Code Listing 12: Solution to Example 6.4 (version 7).

Running the main program will produce:


Enter value of a: 5
Enter value of x: 10
Enter value of n: 6
term = 5, total = 5
term = 60, total = 65
term = 700, total = 765
term = 8000, total = 8765
term = 90000, total = 98765
term = 1000000, total = 1098765
term = 11000000, total = 12098765

Console 7: Output of Example 6.4 (Version 7).

It seems we are progressing faster than what we initially thought. We are ready now to move to the last step.

6.4.4 Printing the series

There are several formatting steps here, but that should not be an issue because again we can start from partial formatting to full formatting.

Let us print the terms one after the other, all in one line, separated by the + operator.


def print_series(a,x,n):
    total = 0
    for i in range(n+1):
        series_term = (a + i)*(x**i)
        total += series_term
        print(f'({a}+{i})*{x}**{i} + ',end='')
    return

Code Listing 13: Solution to Example 6.4 (version 8).

If we run the program, we get:


(5+0)*10**0 + (5+1)*10**1 + (5+2)*10**2 + (5+3)*10**3 + (5+4)*10**4 + (5+5)*10**5 + (5+6)*10**6 +

Console 8: Output of Example 6.4 (Version 8).

This is not quite what we want. But we can improve it.

For the first term, we can simply write it in the form a. We can do this by adding an if statement to handle this special case.

The code becomes:


def print_series(a,x,n):
    total = 0
    for i in range(n+1):
        series_term = (a + i)*(x**i)
        total += series_term
        if i == 0:
            print(f'{a} + ', end = '')
        else:
            print(f'({a}+{i})*{x}**{i} + ',end='')
    return

Code Listing 14: Solution to Example 6.4 (version 9).

Which produces the following output:


5 + (5+1)*10**1 + (5+2)*10**2 + (5+3)*10**3 + (5+4)*10**4 + (5+5)*10**5 + (5+6)*10**6 +

Console 9: Output of Example 6.4 (Version 9).

Similarly, we can add a special case for the last term to print the operator = instead of the plus operator. The code becomes:


def print_series(a,x,n):
    total = 0
    for i in range(n+1):
        series_term = (a + i)*(x**i)
        total += series_term
        
        if i == 0:
            print(f'{a}', end = '')
        else:
            print(f'(a{}+{i})*{x}**{i}',end='')
        
        if i == n:
            print(' = ', end='')
        else:
            print(' + ',end='')
    return

Code Listing 15: Solution to Example 6.4 (version 10).

Observe how we broke the formatting into two if statements to provide better readability. The output becomes:


5 + (5+1)*10**1 + (5+2)*10**2 + (5+3)*10**3 + (5+4)*10**4 + (5+5)*10**5 + (5+6)*10**6 =

Console 10: Output of Example 6.4 (Version 10).

Finally, you can add the total at the end of the line using a print statement outside the for loop. The code becomes:


def print_series(a,x,n):
    total = 0
    for i in range(n+1):
        series_term = (a + i)*(x**i)
        total += series_term
        
        if i == 0:
            print(f'{a}', end = '')
        else:
            print(f'({a}+{i})*{x}**{i}',end='')
        
        if i == n:
            print(' = ', end='')
        else:
            print(' + ',end='')
    print(total)
    return

Code Listing 16: Solution to Example 6.4 (Complete Solution).

The final output will be:


Enter value of a: 5
Enter value of x: 10
Enter value of n: 6
5 + (5+1)*10**1 + (5+2)*10**2 + (5+3)*10**3 + (5+4)*10**4 + (5+5)*10**5 + (5+6)*10**6 = 12098765

Console 11: Final Output of Example 6.4.

This completes the solution.

Additional Operations

In this section, we will present some additional operations that will enhance your use of the for statement. Although some of these operations are not essential to your understanding of the for statement, they would offer you some flexibility and efficiency when writing your code. We will present three operations: the throwaway variables, the break-continue, and the for-else statement.

6.5.1 Throwaway Variables

In Python, there are instances when you need to define a variable due to syntax requirements, but you end up never using it. Such variables are required to be there, but they are redundant. We call these variables general purpose variables or (as many programmers in the Python community prefer to call it) throwaway variables

Suppose you would like to print the statement: 'Wear your seatbelt' ten times, each appearing on a separate line. This could be done using a for statement like the following:


for i in range(10):
    print(‘Wear your seatbelt’)

In the above loop, the variable i is redundant, because it is never used in the inner block. For such instances, we can use the following code instead:


for _ in range(10):
    print(‘Wear your seatbelt’)

The underscore in the above statement communicates to Python that you do not care about that variable. Therefore, Python will discard it. However, it still needs to be there due to the syntax structure of the for statement.

Technical Note:

The throwaway variable is not a specific feature to the for statement. It could also be used in other statements in Python.

Suppose you have the function find_max_min, which returns the maximum and minimum values from a sequence of numbers. The syntax for calling the function would be:

max_val, min_val = find_max_min(num_seq)

At a specific line of code, if you are only interested in the minimum value, then you can write:

_, min_val = find_max_min(num_seq)

This tells Python to discard the returned max_value, because you are not interested in using it.

6.5.2 break and continue statements

In Lesson 6, you were introduced to the break and continue commands in the scope of the while loop statement. Both of these commands alter the flow of execution, by either forcing an exit from the loop (break) or skipping the remaining lines in the loop (continue).

The same concept applies to the for statement. Since you are familiar with the concept, we will limit the discussion to presenting an example that demonstrates how both commands are used in for statements.

Study the following code:


 # Program Name: Prog 6-08
 
 def login():
    print('System is loading ....')
    print('Login screen displayed')
 
    failed_attempts = 0
    for _ in range(8):
       username = input('Enter username: ')
    password = input('Enter password: ')
 
    if username == '' or password == '':
       print('One of the fields is empty. Try again')
     Continue
1 
  validated = validate_user(username,password)
  if not validated:
     failed_attempts += 1
1 
   if failed_attempts == 3:
     print('Your account is locked. Contact your administrator')
     Break
2 
  if validated:
     load_system()
     logout()
     Break
2 
print('System is closing')
30. return
3 
3 def load_system():
3 #some actions to the load system
3 #is called when user is validated
3 return 
3 
3 def logout():
3 #some actions to log the user out of system
3 Return
40. 
4 def validate_user(username,password):
4 #some actions to see if username/password match
4 return

Code Listing 18: Example on using break-continue in a for statement.

The program might seem long, but when you analyze it you will find it is simpler than it appears. The program mainly focuses on the login function. We are assuming here that there are three other functions: logout, validate_user and load_system, which we know work, but we are not interested in knowing how they do that.

Below is a line-by line analysis:

Lines 4,5 Print statements informing the user that the system is loading and showing the login welcome screen.
Line 7 Initializing the variable failed_attempts. This variable will keep track of how many times the user provides a username/password combination that does not match.
Lines 8 A for loop statement. This statement will make the block of code loop 8 times unless it is interrupted. Observe how the throwaway variable is being used here.
Lines 9, 10 Input statements to get a username and a password from the user.
Lines 12-14 If the user forgets to enter the username or password (or both), the loop 'continues' meaning it goes back to line 8 asking the user to re-enter their username and password. Observe that because the loop has only 8 iterations, then the user has only 8 chances to leave one field empty. After 8 iterations, the loop stops and the system closes (line 29).
Lines 16-18 The validate_user function is called to check if the username and password match. If it matches, the function returns True; otherwise, it is False. If the result is False, the failed_attempts variable is incremented.
Lines 20-22 If the user provided a mismatched username/password three times, the loop is interrupted and the program jumps to line 29.
Lines 24-27 If the user is validated, then the system is loaded, and once the user finishes, the logout function is called and the loop interrupts.
Line 29 A print statement indicating that the program is closing.
Lines 32-43 Empty implementation for the functions logout, validate_user and load_system.

The above code does elaborate how break and continue are used in for loops. However, it might have given you some discomfort on how it is structured. You might be thinking that there are better ways to implement the function. Your intuition is correct. Specifically, in the for statement, we want to avoid using break and continue statements unless they are necessary. We interpret 'necessary' here as having code, which would be considered 'worse' if break/continue were not used.  Again, the term 'worse' here is subjective and could refer to various things like readability, efficiency or simplicity.

6.5.3 The for-else Statement

An extension to the break statement, Python offers a special structure called for-else. The syntax of this statement is as follows:


for <var> in  <iterable>:
    <block code 1>
else:
    <block code 2>

The for loop block (Block code 1) will execute normally, and once it completes it will go to the else statement and execute (Block code 2). Right! This sounds confusing because it is different than the if-else statement, when only one block will be executed.

However, if there is a break statement in <block code 1> and it was executed, then the program exits the for loop and also jumps over the else statement.

Therefore, we can read the statement as follows:

Execute the for loop. If the loop completes normally, execute the else block. But if the loop is interrupted by a break statement, then exit the loop and the else blocks.

Using the for-else statement is discouraged, and it is rarely used by Python programmers. Therefore, we will not elaborate further on the discussion. It was presented in this Lesson for completeness purposes and for readability purposes in case you encounter code that uses such structure.

Nested for Loops

Similar to the if and while statements, it is possible to have a for loop enclosed within another for loop. We can also have a for loop inside a while loop and vice versa. The Python language does not put a limit on how many layers we can use. However, we will limit the discussion to a single loop inside another loop.

In Lesson 11, when you study the topic of two dimensional lists, you will frequently use a nested for loop. For now, we will present an example to demonstrate the concept.

Example 6.6:

Write a Python program to draw a rectangle of given length and width. The width represents the number of lines (rows), while the length represents the number of columns. Use the character * to draw the rectangle.

The above example is a classical problem for using a nested for loop. We need to construct two loops. The outer loop controls the printing of lines (rows), and the inner loop controls the number of columns. Let us have a look at the code:


# Program Name: Prog 6-09
# Solution to Example 6.6
  
def draw_rectangle(length,width):
    for _ in range(width):
        for _ in range(length):
            print('*',end='')
        print()
    return

length = int(input('Enter length: '))
width = int(input('Enter width: '))
draw_rectangle(length,width)

Code Listing 19: Solution to Example 7.6 on using nested for loops.

The outer loop does two things: calling the inner loop and printing a new line once the inner loop is complete. The inner loop controls how many * to print in each iteration. The combined effort of both loops would produce an output similar to the following:


Enter length: 16
Enter width: 4
****************
****************
****************
****************

Console 12: Output of Prog 6-09.

You might want to experiment with passing negative values or 0 as length and width and check the output.

Conclusion

In Lesson 6, you learned about the second iterative command in Python: the for statement. You can now read and write commands using for loops.You should understand now the relationship between the for statement and iterable objects in Python. Also, you understand how range operates in a for statement and how it is used to generate number sequences. Finally, you were introduced to some additional features along with how to identify problems which are better solved using a for statement.

In the next lesson, you will learn while loops.

Check Weekly Schedule for Assessments

6.7.2 References

  1. Peter Wentworth, Jeffrey Elkner, Allen B. Downey and Chris Meyers. (October 2012). How to think like a Computer Scientist. Learning with Python 3: Chapter 7: Iterations.Open Book Project. ACM50, 1 Retrieved on July 15, 20
  2. Python Software Foundation (No date). 8.3 The for statement. Retrieved on August 5, 2020.