How to Write a Loop in Python: While and For

How to Write a Loop in Python Featured Image

As this series grows, I often find myself revisiting the fundamentals. For instance, today we’ll be learning how to write a loop in Python. Luckily for you, there’s some bonus material on recursion as well.

In short, there are two core ways of writing a loop, while and for. If you’re looking for a tradition loop, opt for the while loop. Meanwhile, if you have some sequence or iterable to traverse, opt for the for loop. If you find a scenario which gets messy with a loop (e.g. tree traversal), don’t be afraid to fall back on recursion.

Table of Contents

Problem Description

When you first get into programming, you often go through a progression of different pieces of syntax. For instance, you might learn about printing and variables. Then, you might expand your knowledge into arithmetic and boolean expressions. If all goes well, you might even learn about conditionals.

As time goes on, you might ask yourself “but, what if I want to do something repeatedly?” Luckily, most imperative programming languages have a syntax for this called looping. Essentially, we repeat a task until we satisfy some condition.

Of course, if you’ve come from another programming language, you already know all about looping (or at least recursion). The trouble is getting used to the new syntax. Fortunately, we have several different solutions which we’ll take a look at in the next section.

Solutions

In this section, we’ll take a look at three different ways to write a loop in Python. First, we’ll look at recursion, a functional technique. Then, we’ll dive into the two iterative techniques, while and for.

Recursion

Before we dig into the various loop syntax in Python, I feel like it’s important to mention recursion as a concept. After all, we don’t actually need loops at all. We can get away from writing functions which reference themselves:

def recurse():
    recurse()

In this example, we’ve written a function called recurse() which calls itself. If we run it, however, we’ll get an error:

>>> recurse()
Traceback (most recent call last):
  File "<pyshell#2>", line 1, in <module>
    recurse()
  File "<pyshell#1>", line 2, in recurse
    recurse()
  File "<pyshell#1>", line 2, in recurse
    recurse()
  File "<pyshell#1>", line 2, in recurse
    recurse()
  [Previous line repeated 991 more times]
RecursionError: maximum recursion depth exceeded

Of course, this makes sense. After all, if a function calls itself, then it will call itself, then it will call itself, then it will call itself… alright, my head is spinning.

Luckily, this is pretty easy to fix. We just need to add a condition which only calls the function under certain conditions (e.g. while the input is greater than zero):

def recurse(i):
    if i > 0:
        recurse(i - 1)

Now, if we can this function with some number, we won’t crash:

>>> recurse(5)

But, what is this actually doing? Well, let’s try printing something:

def recurse(i):
    print(f'Input is {i}')
    if i > 0:
        recurse(i - 1)

Here, we used an f-string (learn more about those here) to show the input every time this function is called:

>>> recurse(5)
Input is 5
Input is 4
Input is 3
Input is 2
Input is 1
Input is 0

Check that out! We managed to create a function which executes 6 times when we enter a 5. As you can probably imagine, this mechanism can be used to do a lot of interesting things. If you’re interested in learning more about recursion, I’ve written an article all about it.

While Loop

With recursion out of the way, let’s talking about loops. In Python, there are two main looping mechanisms: while and for. Typically, courses cover while first because it’s simpler. If you’re familiar with if statements, a while loop looks almost exactly the same:

while condition:
    do_thing()

If the condition is true, the loop body executes just like an if statement. However, after the body executes, the condition is rechecked. If the condition is still true, we drop back into the loop body once again.

Naturally, we can write a loop which behaviors similarly to our recursion example. All we have to do is create a counter variable and count down on each iteration:

i = 5
while i >= 0:
    print(f'Input is {i}')
    i -= 1

In this example, we create a variable called i and give it a value of 5. Then, we kick off the loop by checking if i is greater than or equal to 0. Since it is, we drop into the loop where we print “Input is 5” and decrement i. Then, the process repeats. Of course, now i is 4 instead of 5. Overall time, i will decrement until it is -1, and the loop condition will fail.

In Python, while can be used to implement any indefinite loop. In other words, use a while loop when you don’t know how many iterations you’ll have ahead of time. For example, while loops are perfect for reading from files or prompting for input from a user. In the next section, we’ll take a look at an example of a definite loop.

For Loop

In many imperative languages like Java, C, and Python, there is more than one way to write a loop. For example, in Java, there are at least four different loop syntaxes that I’m aware of (e.g. while, for, for each, do while). Since Python tries to keep things simple, the number of loop syntaxes are limited. As far as I know, there are only two: for and while.

Now, for loops in Python aren’t like for loops in other languages. Instead of providing a space to track an index, they operate more like for each loops in other languages. In other words, we need something to iterate over like a list. Let’s try recreating our while loop from above:

indices = [5, 4, 3, 2, 1, 0]
for i in indices:
    print(f'Input is {i}')

To make this loop work, we had to create a list to iterate over. Clearly, this isn’t as convenient as the previous solution. Luckily, Python has a way generating these sort of iterables:

for i in range(5, -1, -1):
    print(f'Input is {i}')

Here, we’ve created a loop which will count down from 5 to 0 just like all our other loops. To do that, we used the range() function which generates a list-like structure from the inputs provided. In this case, 5 represents the inclusive starting value, the first -1 represents the exclusive ending value, and the second -1 represents the step (i.e. how many values to skip and in what direction).

In general, for loops are more useful for iterating over sequences like lists, strings, or generators. In other words, they don’t work exactly like for loops in other languages—not without using a special function like range().

Performance

At this point, I’m not sure it makes sense to compare the performance of these three constructs, but I already wrote three solutions that do the same thing. In other words, we’re just begging for a comparison. To kick things off, let’s store all three of our solutions in strings:

setup = """
i = 5
def recurse(i):
    # Removed print for sanity
    if i > 0:
        recurse(i - 1)
"""

recursion = """
recurse(5)
"""

while_loop = """
while i >= 0:
    # Removed print for sanity
    i -= 1
"""

for_loop = """
for i in range(5, -1, -1):
    pass  # Removed print for sanity
"""

Then, we can run out test as follows:

>>> import timeit
>>> min(timeit.repeat(setup=setup, stmt=recursion))
0.7848201999999986
>>> min(timeit.repeat(setup=setup, stmt=while_loop))
0.040824499999999375
>>> min(timeit.repeat(setup=setup, stmt=for_loop))
0.34835850000000335

One thing I found really interesting was the performance of the while loop. Then, I realized that my test was slightly inaccurate. Specifically, I had placed the i in setup, so it became zero after the first iteration. In other words, the while loop became a glorified if statement. When I updated my setup string, here were the results:

>>> setup = """
def recurse(i):
    # Removed print for sanity
    if i > 0:
        recurse(i - 1)
"""
>>> while_loop = """
i = 5
while i >= 0:
    # Removed print for sanity
    i -= 1
"""
>>> min(timeit.repeat(setup=setup, stmt=while_loop))
0.3415355000000204

Now, that’s almost identical to the for loop—which makes sense to me. That said, I was reading some performance discussions on StackOverflow, and the for loop should be faster overall. Naturally, I had to investigate, so I updated both solutions for large numbers:

>>> for_loop = """
for i in range(100, -1, -1):
    pass  # Removed print for sanity
"""
>>> min(timeit.repeat(setup=setup, stmt=for_loop))
1.2956954000001133
>>> while_loop = """
i = 100
while i >= 0:
    # Removed print for sanity
    i -= 1
"""
>>> min(timeit.repeat(setup=setup, stmt=while_loop))
4.765163399999892

Turns out that 100 was all I was willing to wait. Otherwise, this test may have taken all day. That said, even at a number this small, there’s an obvious difference in performance. Feel free to check out that discussion above for a further explanation of why.

Challenge

Now that we know how to write a loop, let’s try something interesting. Let’s imagine we have a list of lists (aka a matrix):

my_matrix = [
    [3, 5, 2, 4],
    [5, 9, 4, 2],
    [1, 8, 4, 3]
]

And, we want to total each row (inner list) and determine the average of all rows. Using the example above, we’d get the following row totals:

my_matrix = [
    [3, 5, 2, 4],  # 14
    [5, 9, 4, 2],  # 20
    [1, 8, 4, 3]   # 16
]

Then, we’d average the totals:

(14 + 20 + 16) / 3  # 16.666666666666668

When we’re done, we’d report the result to the user.

While this seems like a pretty straightforward task for us, how would we train the computer to do it? In other words, how would we use the various loop syntaxes to do this (hint: you’ll might want to nest two loops)?

If you come up with a solution, drop it down below in the comments. Naturally, I’ll throw my own solution down there to get us started.

A Little Recap

With all that out of the way, let’s revisit our solutions once again:

# Recursion
def recurse(i):
    print(f'Input is {i}')
    if i > 0:
        recurse(i - 1)
recurse(5)

# While loop
i = 5
while i >= 0:
    print(f'Input is {i}')
    i -= 1

# For loop
for i in range(5, -1, -1):
    print(f'Input is {i}')

If you liked this article, you might like joining the weekly mailing list or becoming a Patron. Otherwise, stick around and check out some of these related articles:

In addition, you might get some value out of the following products on Amazon (ad):

If none of that sounds interesting, no sweat! Thanks for checking out my work today.

Series Navigation← How to Comment Code in Python: Inline, Multiline, and DocstringHow to Compare Strings in Python: Equality and Identity →

Jeremy Grifski

Jeremy grew up in a small town where he enjoyed playing soccer and video games, practicing taekwondo, and trading Pokémon cards. Once out of the nest, he pursued a Bachelors in Computer Engineering with a minor in Game Design. After college, he spent about two years writing software for a major engineering company. Today, he pursues a PhD in Engineering Education in order to ultimately land a teaching gig. In his spare time, Jeremy enjoys spending time with his wife, playing Overwatch and Phantasy Star Online 2, practicing trombone, watching Penguins hockey, and traveling the world.

Recent Content