There are so many ways to use for loops.
In this lesson, we will see a few common patterns.
The patterns shown here are just starting points.
Like the if patterns we saw before, think of these patterns as templates that can be adapted and combined to solve more complex problems.
a_list = ["Alpha", "Beta", "Gamma"]
count = 0
for item in a_list:
count = count + 1
print(count)
We have a list of items and want to know how many there are. A simple algorithm is:
Start with an initial value of 0.
For each element, add 1 to a count variable.
When the loop is finished, the count variable will have the length of the list.
The code shown here demonstrates how the pattern is written in practice.
Note that the item iteration variable is never actually used, unlike in other patterns!
a_list = [10, 30, 20]
sum = 0
for item in a_list:
sum = sum + item
print(sum)
We have a list of numbers and want to add them all up. The plus operator can only take two items at a time, however. Therefore, we add each element one at a time to a “sum” variable, which is also initialized to 0. As you can see, the sum pattern is like the count pattern, except instead of adding 1, we are adding the iteration variable.
result = ___
for item in a_list:
result = result ___ item
The sum and count patterns are both more specific examples of the accumulate pattern. In general, this pattern allows us to start with an initial value and use any function or operation that takes in two values. This process of accumulation is also sometimes known as “reducing” or “folding” a list. This pattern can be applied to numbers, but it also works for strings, Booleans, and any type that can be combined with an operator that takes two operands. You must think critically about what the initial value should be and what operator you will use to combine them.
sentence = "" # Start with an empty string
words = ["Hello, ", "how ", "are ", "you?"]
# Add the words one by one
for word in words:
sentence += word
print(sentence)
# Output: Hello, how are you?
Technically, string accumulation is no different than the summation pattern. The only difference is that strings are added together instead of integers. The result is a single string with all the source strings joined together. So, string accumulation is the process of gradually building up a string by adding more characters or other strings to it.
answers = [True, True, False, True]
# Any True?
any_true = False
for answer in answers:
any_true = any_true or answer
print(any_true)
# All True?
all_true = True
for answer in answers:
all_true = all_true and answer
print(all_true)
The Boolean accumulation patterns come in two versions: the “any” and the “all.” With “any,” we start with an initial value of False.
Then we check if either the current element is True or if the accumulation variable is True.
If at any point we encounter a True value in the list, then the accumulation variable becomes True and will evaluate to True in every subsequent check.
On the other hand, the “all” variant starts off with the initial value of True.
Each check is whether the current element is True and if the accumulation variable is still True.
If at any point we encounter a False value in the list, then the accumulation variable becomes False and will evaluate to False in every subsequent check.
from bakery import assert_equal
def any_red(colors: list[str]) -> bool:
result = False
for color in colors:
result = result or color == "red"
return result
assert_equal(any_red(["blue", "red", "green"]), True)
assert_equal(any_red(["r", "g", "b"]), False)
Boolean accumulation is more powerful than it appears at first.
The key insight is that the pattern works on more than just a list of Boolean values.
You can use an expression that produces a Boolean to ask a question about each element of a list.
Note that no if statement is required; we are simply using or to combine logical conditions together and return the final Boolean result.
old_list = ["Apple", "Grape", "Orange"]
copied_list = []
for item in old_list:
copied_list.append(item)
print(copied_list)
What happens when we accumulate a list? If we start with an empty list as our initial value and append each value one at a time, we end up with a copy of the original list. We could then make further modifications to the new list without worrying about changing the original list. This is the key idea of the map pattern.
fruits = ["Apple", "Grape", "Orange"]
plural_fruits = []
for fruit in fruits:
plural_fruits.append(fruit + "s")
print(plural_fruits)
As we’re appending values, we can also modify them.
For example, you could double each value from the old list, or convert each temperature from Fahrenheit to Celsius.
In this program, we are simply adding the string "s" to the end of each fruit to make the plural version of the word.
However, any valid operation or expression can be used as arguments to the append method, allowing us to transform each element of the list.
fruits = ["Apple", "Grape", "Orange"]
plural_fruits = []
for fruit in fruits:
# Uh oh, incorrect accumulation variable!
fruits.append(fruit + "s")
print(plural_fruits)
Do you see the mistake in the program shown here? If you tried running it, you would find your program taking a very long time.
In fact, it might take forever! The problem is that we are appending to the very same list that we are iterating over.
If you append to the fruits list while you iterate through the fruits list, the for loop will keep finding new elements to process.
Python is more than happy to keep going around in circles until you stop the program or the end of time - whichever comes first.
numbers = [50, 100, 200]
# Filter+Map
new_list = []
for number in numbers:
if number < 100:
new_list.append(number)
print(new_list)
We have a list of numbers and want to ignore some of them according to a rule.
By embedding an IF statement inside the loop, we can optionally include or not include elements in our accumulation.
This is the filter pattern, which is more of an optional modification we can make to our other patterns.
In the example shown, we have the filter pattern combined with the map pattern.
Tracing the flow of this program can be a little tricky, but keep in mind that it’s the same rules as we’ve seen before.
The body of the for loop is repeated however many times there are items in the list.
Each time, the if statement’s condition is considered, and if True then its body with the append statement is executed.
When the for loop is entirely done, we execute the print statement after the for loop.
numbers = [50, 100, 200]
# Filter + Count
result = 0
for number in numbers:
if number <= 100:
result = result + 1
print(result)
The filter pattern is very compatible with the other patterns.
In the example shown, we have a variant of the pattern being combined with the count pattern.
The basic code structure is the same, with an if statement inside of our for loop.
The big difference comes in the initial value and accumulation step, which match the count pattern instead of the map pattern.
# Before
for item in a_list:
# Inside
pass
# After
Novices struggle with what goes inside or outside of a loop body. Remember: Every statement inside the body is executed for each element. Only put things inside if they should happen for each element. The patterns can help you keep track of where things go, but ultimately, you must think critically to know.
from bakery import assert_equal
def count_3s(numbers: list[int]) -> int:
total = 0
for number in numbers:
if number == 3:
total = total+1
return total
assert_equal(count_3s([1, 2, 3]), 1)
assert_equal(count_3s([3, 3, 3, 3]), 4)
Often, you will use these patterns inside of functions that consume lists.
All the same rules about indentation and scope apply as before.
However, it can be confusing to put these complex pieces together.
It may not be clear where the return statement goes relative to the body of the for loop.
In the example program shown here, you will notice that the return statement goes AFTER the body of the for loop, OUTSIDE of the if statement, but INSIDE the body of the function definition.
for loop patterns that can help you structure programs that operate on lists of data: