For Loops in Python

Learning objectives

In the previous lesson, we learned how Python implements while loops and integrated a while loop into a factorial() function.

In the core text, we also saw that we could write a function to calculate n! using a for loop, as shown by the following pseudocode function AnotherFactorial().

AnotherFactorial(n)
    p ← 1
    for every integer i between 1 and n
        p ← p·i
    return p

In this lesson, we will learn how Python implements for loops, which will prepare us to implement our two GCD algorithms from the main text.

Code along summary

Set up

Create a new folder called for_loops in your python/src directory, and then create a new file within the python/src/for_loops folder called main.py, which we will edit in this lesson.

In main.py, let us add the following starter code that is comparable to our starter code in previous lessons.

def main():
    print("For loops.")


if __name__ == "__main__":
    main()

Introducing for loops in Python: another factorial function

Recall the AnotherFactorial() pseudocode function from the main text, shown below, which uses a for loop instead of a while loop.

AnotherFactorial(n)
    p ← 1
    for every integer i between 1 and n
        p ← p·i
    return p

Our Python implementation of this function is shown below. As we might guess, the for loop will begin with a colon and be indented in the same manner as while loops.

The syntax that we use in Python to indicate that we want to take an action for every integer i between 1 and n is for i in range(1, n+1). This syntax might seem odd, since we want to end the ranging at n, not at n + 1. However, Python uses an indexing convention adopted by most modern programming languages that when considering the range of integers between a and b, the range includes a but does not include b. That is, range(a, b) yields the integers a, a+1, …, b-1.

def another_factorial(n: int) -> int:
    """
    Takes as input an integer n and returns n! = 1*2*...*(n-2)*(n-1)*n using a for loop.

    Parameters:
    - n (int): an integer

    Returns:
    int: n! = 1*2*n*(n-1)*(n-2)*...*2*1
    """

    if n < 0:
        raise ValueError("Error: negative input given to another_factorial().")

    p = 1

    # for every integer i between 1 and n, p = p * i
    for i in range(1, n + 1):
        p *= i

    return p

We will now zoom in on the for loop itself, which uses the shorthand syntax p *= i introduced in the previous lesson.

    # for every integer i between 1 and n, p = p * i
    for i in range(1, n + 1):
        p *= i

The for loop consists of three parts.

  1. The variable: (i): this establishes a variable that takes on the value of each element in the iterable.
  2. The iterable (range(1, n+1)): this is an object that is capable of returning a sequence of elements. In this case, we generate a sequence of numbers starting from 1, up to and including n.
  3. The code block: the indented block of code following the colon is the body of the loop. This code is executed for each element in the iterable.

Let’s ensure that another_factorial() is working, by adding the following code to main().

def main():
    print("For loops.")

    n = 5
    m = another_factorial(n)
    print(m)
STOP: Open a terminal window, and navigate into the folder containing main.py by executing cd python/src/for_loops. Then run your code by executing python3 main.py (macOS/Linux) or python main.py (Windows). You should see 120 printed.
Click Run 👇 to try it!

A stylistic point about for and while loops

STOP: Consider our two functions for computing n!, reproduced below. Are they equivalent, or do you prefer one over the other?
def factorial(n: int)  -> int:
    """
    Takes as input an integer n and returns n! = n*(n-1)*(n-2)*...*2*1 using a while loop.

    Parameters:
    - n (int): an integer

    Returns:
    int: n! = n*(n-1)*(n-2)*...*2*1
    """

    if n < 0:  # let's handle negative input
        raise ValueError("Error: negative input given to factorial().")

    p = 1
    i = 1

    while i <= n:
        p = p * i
        i = i + 1

    return p
def another_factorial(n: int)  -> int:
    """
    Takes as input an integer n and returns n! = n*(n-1)*(n-2)*...*2*1 using a for loop.

    Parameters:
    - n (int): an integer

    Returns:
    int: n! = 1*2*...*(n-2)*(n-1)*n
    """

    if n < 0:
        raise ValueError("Error: negative input given to another_factorial().")

    p = 1

    # for every integer i between 1 and n, p = p * i
    for i in range(1, n + 1):
        p *= i

    return p

In this case, although the two functions are similar, another_factorial() is slightly more preferable because it does not need to declare the variable i before the loop is initiated. On the whole, any time that we can use a for loop instead of a while loop, we will typically do so. Furthermore, as we will see soon, for loops let us range over complex data structures with ease.

Yet we should also point out that while loops are more general than for loops. In the case of factorial(), we could easily convert a while loop into a for loop. However, consider the following function pittsburgh_february(), which simulates life in Pittsburgh during the winter. This function incorporates randomness, an idea that we will explore in depth in a later chapter. For now, we point out that each day, there is an 80 percent chance of snow, which we simulate by generating a random number between 0 and 1. If this number is less than 0.8, then we set a variable dream equal to True and dream of moving; if the number is greater than or equal to 0.8, we stop dreaming and make our move.

We cannot write pittsburgh_february() using a for loop because we don’t know in advance how many days we will continue to dream. We may escape Pittsburgh on the first day, or we may need to wait a month; you can see this by running the code multiple times to obtain differing results.

As a general rule, if we know the number of iterations that we will make in advance, then we will use a for loop. If we are waiting for a condition (user input, randomness, convergence), then we will use a while loop.

import random

def pittsburgh_february():
    """
    Simulates snow in Pittsburgh during February.
    Each day has an 80% chance of snow. The loop continues
    as long as it is snowing, and ends on the first day without snow.
    """
    day = 1
    dream = True  # start February dreaming

    while dream:
        if random.random() < 0.8:
            print("It is February", day, "and it is snowing.")
            print("Dream of moving to Florida.")
            day += 1
        else:
            dream = False

    print("Finally, no snow! Moving to Florida.")
Click Run 👇 to try it!

Variations of for loops

The range() function is a versatile tool that can serve various purposes. As we have seen, when using range() with two parameters, the first parameter denotes the starting value of the sequence (inclusive), and the second parameter represents the ending value (exclusive). By default, the range() function generates a sequence of numbers in increasing order, starting from the specified start value up to (but not including) the end value.

However, the range() function becomes even more flexible when a third parameter is introduced. This third parameter is the step size, determining the interval between each value in the sequence. If the step size is positive, the sequence increments by the value specified; if negative, the sequence decrements by the value specified.

For example, we could use this three-parameter notation in another_factorial() to range downward from n to 1, decrementing i by 1 in each iteration. To do so, we would use the notation for i in range(n, 0, -1); this shorthand indicates that we start i as equal to n, we decrement i by 1 in each iteration, and we continue until i is equal to 0, at which time we no longer enter the for loop.

def yet_another_factorial(n: int)  -> int:
    """
    Takes as input an integer n and returns n! = n*(n-1)*(n-2)*...*2*1 using a for loop.

    Parameters:
    - n (int): an integer

    Returns:
    int: n! = n*(n-1)*(n-2)*...*2*1
    """

    if n < 0:
        raise ValueError("Error: negative input given to yet_another_factorial().")

    p = 1

    # for every integer i from n (inclusive) to 0 (exclusive), decrementing by 1 in each iteration
    for i in range(n, 0, -1):
        p *= i

    return p
Click Run 👇 to try it!
STOP: How would another_factorial() need to change if we applied the same approach to edit its for loop?

Another example of our more generic ranging is offered by the following exercise.

Exercise: Write a function sum_even() that takes an integer k as input and returns the sum of all even positive integers up to and possibly including k.

Recall from the main text that an integer p is a divisor of an integer n if the remainder after dividing n by p is zero. Furthermore, we asked you to write pseudocode functions IntegerDivision() and Remainder(), both of which have integer parameters n and p, and which respectively return the integer component and the remainder when dividing n by p. Fortunately, as we have seen in a previous code along, Python has built-in operations for both of these functions: integer division is given by n//p, and the remainder after this division is given by n%p.

Note: The remainder operation n % p is often called “n modulo p”.

We can therefore implement sum_even() by ranging over all integers j between 2 and k, and checking if j is even using the test if j % 2 == 0. We add each such j to a variable s, which we will return.

def sum_even(k: int) -> int:
    """
    Takes as input an integer k and returns the sum of all even positive integers up to and (possibly) including k.

    Parameters:
    - k (int): an integer

    Returns:
    int: sum of all even positive integers up to and (possibly) including k
    """

    s = 0

    # for every even integer j between 2 and k, s = s + j
    for j in range(2, k + 1):
        if j % 2 == 0:
            s += j

    return s
Click Run 👇 to try it!

Yet we can solve this problem much faster by ignoring all odd integers entirely. To do so, we specify the step size to be 2, because if we add 2 to j each time, we know that j will always be even.

def sum_even(k: int) -> int:
    """
    Takes as input an integer k and returns the sum of all even positive integers up to and (possibly) including k.

    Parameters:
    - k (int): an integer

    Returns:
    int: sum of all even positive integers up to and (possibly) including k
    """

    s = 0

    # for every even integer j between 2 and k, sum_val = sum_val + j
    for j in range(2, k + 1, 2):
        s += j

    return s
Click Run 👇 to try it!

In addition to a three-parameter version of range(), we have a one-parameter version as well. To range between all integers from 0 up to but not including n, we can use the notation range(n). This is useful not only in this specific instance, but also in the more generic case that we just want to run a for loop n times without having an explicit need for a variable keeping track of how many times we have been through the loop. For example, if we wanted to print the statement Hello, World! five times, we can use the following code:

def say_hi_five_times():
    # print Hello, World! five times
    for i in range(5):
        print("Hello, World!")

Finally, we would note that in this case, the variable i is not needed at all. When this is the case, we can use the underscore symbol (_) in place of an index variable to indicate that we don’t care about the index of the for loop, as shown below.

def say_hi_five_times():
    # print Hello, World! five times
    for _ in range(5):
        print("Hello, World!")
Click Run 👇 to try it!

You made it!

Thus ends our introduction to the basics of Python. You should be proud of making it this far because although this introduction is relatively short, everything else that we learn about in this course will build upon it.

We are now ready to return to start implementing our two nontrivial algorithms from the main text. In the next lesson, we begin with Euclid’s algorithm for calculating the GCD of two integers.

Check your work from the code along

We provide autograders in the window below (or via a direct link) allowing you to check your work for the following functions:

  • another_factorial()
  • sum_even()

powered by Advanced iFrame


Page Contents
Scroll to Top