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
andfor
. If you’re looking for a tradition loop, opt for thewhile
loop. Meanwhile, if you have some sequence or iterable to traverse, opt for thefor
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 anotherprogramming 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
andfor
.
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 calledrecurse()
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 thosehere) to show the input every time this function is called:
>>> recurse(5)Input is 5Input is 4Input is 3Input is 2Input is 1Input 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 writtenan 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
andfor
. Typically, courses coverwhile
first because it’s simpler. If you’re familiar with if statements, awhile
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 = 5while i >= 0: print(f'Input is {i}') i -= 1
In this example, we create a variable calledi
and give it a value of 5. Then, we kick off the loop by checking ifi
is greater than or equal to 0. Since it is, we drop into the loop where we print “Input is 5” and decrementi
. Then, the process repeats. Of course, nowi
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 awhile
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
andwhile
.
Now,for
loops in Python aren’t likefor
loops in other languages. Instead of providing a space to track an index, they operate more likefor each
loops in other languages. In other words, we need something to iterate over like a list. Let’s try recreating ourwhile
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 therange()
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 likefor
loops in other languages—not without using a special function likerange()
.
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 = 5def 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 thewhile
loop. Then, I realized that my test was slightly inaccurate. Specifically, I had placed thei
in setup, so it became zero after the first iteration. In other words, thewhile
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 = 5while i >= 0: # Removed print for sanity i -= 1""">>> min(timeit.repeat(setup=setup, stmt=while_loop))0.3415355000000204
Now, that’s almost identical to thefor
loop—which makes sense to me. That said, I was readingsome 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 = 100while 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:
# Recursiondef recurse(i): print(f'Input is {i}') if i > 0: recurse(i - 1)recurse(5)# While loopi = 5while i >= 0: print(f'Input is {i}') i -= 1# For loopfor i in range(5, -1, -1): print(f'Input is {i}')
If you liked this article, you might likejoining the weekly mailing list orbecoming a Patron. Otherwise, stick around and check out some of these related articles:
- How to Format a String in Python
- How to Write a List Comprehension in Python
- How to Sort a List of Strings in Python
In addition, you might get some value out of the following products on Amazon (ad):
- Python and Algorithmic Thinking for the Complete Beginner
- Python Tricks: A Buffet of Awesome Python Features
If none of that sounds interesting, no sweat! Thanks for checking out my work today.
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. Then, he earned a master's in Computer Science and Engineering. Most recently, he earned a PhD in Engineering Education and now works as a Lecturer. In his spare time, Jeremy enjoys spending time with his wife and kid, playing Overwatch 2, Lethal Company, and Baldur's Gate 3, reading manga, watching Penguins hockey, and traveling the world.
Recent Posts
I wanted to take a moment to just talk about how I'm doing, which is really well actually.
The Worst Use Cases for Generative AI That Are Already Mainstream
This article looks at several use cases of generative AI that I find dystopian at best, and I want to talk about them.