List Iteration and Comprehension
Objectives
By the end of this chapter, you should be able to:
- Iterate over lists and strings
- Create and iterate over ranges
- Use list comprehension to write concise loops
- Use a nested comprehension to build a list of lists
💡 Why this matters: You’ll loop over collections constantly, and list comprehension is how experienced Python developers turn a multi-line loop into a single, readable line.
Looping with for...in
The standard way to loop over a list (or a string) is for element in collection:. Don’t forget the colon at the end of that line:
values = [1, 2, 3, 4]
for val in values:
print(val)
# 1
# 2
# 3
# 4
for char in "awesome":
print(char)
# a
# w
# e
# s
# o
# m
# e
Getting the Index Too: enumerate
When you need both the index and the value, wrap the collection in enumerate:
for idx, char in enumerate("awesome"):
print(idx, char)
# 0 a
# 1 w
# 2 e
# 3 s
# 4 o
# 5 m
# 6 e
while Loops
Less common for iterating over a collection, but still useful:
i = 0
while i < 5:
print(i)
i += 1
# 0
# 1
# 2
# 3
# 4
Skipping and Stopping: continue and break
continue skips to the next iteration; break exits the loop entirely:
for num in [1, 2, 3, 4, 5, 6, 7]:
if num % 2 == 0:
continue
elif num > 5:
break
print(num)
# 1
# 3
# 5
Even numbers hit continue and skip straight to the next iteration, before they ever reach print. 7 is the value that actually triggers break: it’s odd, so it passes the continue check, then 7 > 5 is True, so the loop exits immediately, before print(7) runs.
range
range(start, stop, step) generates a sequence of numbers without building a full list in memory, useful for anything that just needs to count. It’s not inclusive of stop:
for num in range(4, 10):
print(num)
# 4
# 5
# 6
# 7
# 8
# 9
range also unpacks like any other sequence:
a, b, c, d = range(4)
a # 0
b # 1
c # 2
d # 3
Combined with chr() (which converts a number into its corresponding character), you can build an alphabet:
capital_letters = []
for num in range(65, 91):
capital_letters.append(chr(num))
capital_letters
# ['A', 'B', 'C', ..., 'X', 'Y', 'Z']
Because a range doesn’t store every number in memory the way a list does, prefer it over a hardcoded list whenever you just need evenly-spaced numbers.
List Comprehension
List comprehensions build a new list in a single line, a much more concise alternative to writing out a full loop. The simplest form transforms every element in a collection:
[num ** 2 for num in range(10)]
# [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
[chr(num) for num in range(65, 91)]
# ['A', 'B', 'C', ..., 'X', 'Y', 'Z']
Add an if clause to filter which elements make it into the result. Here, in is used as a membership check: letter in ["a", "e", "i", "o", "u"] is a True/False question (“is letter one of these?”), which is different from the in used to start a for loop:
"a" in ["a", "e", "i", "o", "u"] # True
"z" in ["a", "e", "i", "o", "u"] # False
# without a comprehension
vowels = []
for letter in "awesome":
if letter in ["a", "e", "i", "o", "u"]:
vowels.append(letter)
# vowels == ['a', 'e', 'o', 'e']
# the same thing, as a comprehension
vowels = [letter for letter in "awesome" if letter in ["a", "e", "i", "o", "u"]]
# ['a', 'e', 'o', 'e']
Comprehensions combine naturally with other functions too. Here’s a comprehension nested inside len(), counting how many three-letter words are in a sentence:
len([word for word in "the quick brown fox jumps over the lazy dog".split(" ") if len(word) == 3])
For anything longer, split the comprehension across multiple lines for readability:
len([
word
for word in "the quick brown fox jumps over the lazy dog".split(" ")
if len(word) == 3
])
The syntax takes some practice to read comfortably. Every comprehension follows the same shape: [expression for item in iterable if condition], where the if condition part is optional.
Nested List Comprehension: Building a List of Lists
A comprehension’s expression (the part before for) can be anything, including another whole list, which gives you a list of lists:
[[0, 1, 2] for _ in range(3)]
# [[0, 1, 2], [0, 1, 2], [0, 1, 2]]
Read this the same way as any other comprehension: for every value produced by range(3), put [0, 1, 2] into the result. The underscore _ is a common convention for “a loop variable I’m not actually going to use”: range(3) is only there to make the loop run three times, not because its values matter.
This is a genuinely different pattern from the filtering/transforming comprehensions above: instead of transforming each existing element, you’re repeating one fixed value a set number of times. Both are “list comprehension,” but it’s worth recognizing them as two distinct uses of the same syntax.
Try It
- Loop over a string with
for...in, printing each character alongside its index usingenumerate. - Build a list of the squares of
0through9using a comprehension. - Use a comprehension with an
iffilter to pull every word longer than four letters out of a sentence of your choice. - Use a nested comprehension to build a list containing 5 copies of the string
"hi".
Recap
for...inis the standard way to loop;enumerateadds the index alongside each element.range(start, stop, step)generates numbers without building a full list in memory, and excludes thestopvalue.- List comprehensions (
[expression for item in iterable if condition]) replace many loops with a single, readable line. - A comprehension’s expression can be a whole list itself, giving you a list of lists: a repetition pattern, distinct from transforming or filtering existing elements.
Next lesson: put lists into practice with a set of exercises, all written using list comprehension.