Warm-up · Activity 1 of 7
// B4.2 · ~25 min · Beginner
List comprehensions
After this lesson you can turn a list-building loop into a comprehension, read comprehensions with two for clauses or a comprehension inside another, and remove items with del.
Predict · Activity 2 of 7
Predict before you read on: what does this print?
print([x * x for x in range(5) if x % 2 == 0])Practice · Activity 3 of 7
Fill in the keyword that keeps only the even numbers.
nums = [4, 7, 10, 13] evens = [n for n in nums ____ n % 2 == 0]evens = [n for n in nums n % 2 == 0]Practice · Activity 4 of 7
Two for clauses. What does this print?
print([x + y for x in "ab" for y in "12"])Practice · Activity 5 of 7
Put the lines in order to build the same list as this comprehension with a for loop.
result = [w.upper() for w in words if w]- 1. result.append(w.upper())
- 2. if w:
- 3.result = []
- 4.for w in words:
Brain teaser · Activity 6 of 7
Brain teaser. Both comprehensions use if. What does this print?
nums = [3, -1, 2] print([x if x > 0 else 0 for x in nums], [x for x in nums if x > 0])Apply · Activity 7 of 7
Mini-task. Write flatten(matrix), which returns all values of a list of lists in one flat list, and transpose(matrix), which turns rows into columns. Use one comprehension in each. For [[1, 2, 3], [4, 5, 6]] you should get [1, 2, 3, 4, 5, 6] and [[1, 4], [2, 5], [3, 6]].
Check your work against this list
Build it yourself
Read the worked example, then write the exercises. Your code runs in your browser or on your computer and is never uploaded.
Worked example
Cleaning survey answers
A survey collected free-text answers with stray spaces, capitals and blanks. One comprehension tidies them and drops the empty ones: strip() removes spaces at both ends, lower() makes the text lower case. A second comprehension keeps only the yes answers. Then a comprehension inside a comprehension builds a small multiplication table, and del removes answers by position and by slice. Change the answers and run it again.
main.py
answers = [" Yes", "no ", "", "YES", " maybe", "No"]
# Tidy every answer and drop the empty ones.
cleaned = [a.strip().lower() for a in answers if a.strip()]
print("Cleaned:", cleaned)
yes_votes = [a for a in cleaned if a == "yes"]
print("Yes votes:", len(yes_votes))
# A comprehension inside a comprehension: one inner list per row.
table = [[row * col for col in range(1, 4)] for row in range(1, 4)]
print("Table:", table)
# del removes by position or by slice, and returns nothing.
del cleaned[0]
print("Without the first:", cleaned)
del cleaned[1:3]
print("Without two more:", cleaned)
Run it with
python main.pyOutput
Cleaned: ['yes', 'no', 'yes', 'maybe', 'no']
Yes votes: 2
Table: [[1, 2, 3], [2, 4, 6], [3, 6, 9]]
Without the first: ['no', 'yes', 'maybe', 'no']
Without two more: ['no', 'no']- The filter a.strip() drops "", because an empty string is false.
- The table has one inner list per row: the outer comprehension runs the inner one three times.
- del cleaned[1:3] removed the items at positions 1 and 2, not 3.
Change it and run it
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Exercises
Exercise 1 of 2
Shout the long words
shout_long(words, min_len) should return, in capitals and in their original order, only the words that have at least min_len letters. The starter capitalises every word. Add the filter, keeping it one list comprehension.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Hints
Hint 1
A filter goes at the end of the comprehension: … for w in words if condition.
Hint 2
len(w) gives the number of letters. "At least" means >=.
Hint 3
return [w.upper() for w in words if len(w) >= min_len]
Show a solution
One way to solve it. Yours can look different and still pass the checks.
def shout_long(words: list[str], min_len: int) -> list[str]:
"""Return the words with at least min_len letters, in capitals."""
return [w.upper() for w in words if len(w) >= min_len]
Run it on your computer
Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.py
def shout_long(words: list[str], min_len: int) -> list[str]:
"""Return the words with at least min_len letters, in capitals."""
return [w.upper() for w in words]
test_main.py
from main import shout_long
def test_filters_short_words():
"""Keeps only words with at least min_len letters, in capitals"""
got = shout_long(["hi", "hello", "hey", "world"], 4)
assert got == ["HELLO", "WORLD"], f"shout_long([...], 4) returned {got!r}, expected ['HELLO', 'WORLD']"
def test_boundary():
"""A word of exactly min_len letters is kept"""
got = shout_long(["tea", "to"], 3)
assert got == ["TEA"], f"shout_long(['tea', 'to'], 3) returned {got!r}, expected ['TEA']"
def test_empty():
"""An empty list gives an empty list"""
got = shout_long([], 2)
assert got == [], f"shout_long([], 2) returned {got!r}, expected []"
On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.
Run the program:
python main.pyRun the checks (needs learnrun.py in the same folder):
python learnrun.py testDownload learnrun.pyExercise 2 of 2
Seat labels
seat_labels(rows, cols) should return one list per row of a theatre, with labels A1, A2, … for the first row, B1, B2, … for the second, and so on. seat_labels(2, 3) is [["A1", "A2", "A3"], ["B1", "B2", "B3"]]. The starter uses two for clauses and gets one flat list. Rewrite it as a comprehension inside a comprehension.
Tab indents and Shift+Tab outdents. To leave the editor with the keyboard, press Esc, then Tab.
The first run downloads Python for your browser (up to 6.5 MB) and keeps it cached. Your code stays on your device.
Hints
Hint 1
The inner comprehension builds one row: [letter + str(number) for number in range(1, cols + 1)].
Hint 2
The outer comprehension runs the inner one once per letter in "ABCDEFGHIJ"[:rows].
Hint 3
return [[letter + str(number) for number in range(1, cols + 1)] for letter in "ABCDEFGHIJ"[:rows]]
Show a solution
One way to solve it. Yours can look different and still pass the checks.
def seat_labels(rows: int, cols: int) -> list[list[str]]:
"""Return one list of seat labels per row: A1, A2, ... then B1, B2, ..."""
return [[letter + str(number) for number in range(1, cols + 1)] for letter in "ABCDEFGHIJ"[:rows]]
Run it on your computer
Install Python 3.14 or newer. Save these files in one folder, open a terminal in that folder, and run the commands below.
main.py
def seat_labels(rows: int, cols: int) -> list[list[str]]:
"""Return one list of seat labels per row: A1, A2, ... then B1, B2, ..."""
# This builds one flat list. Build one inner list per row instead.
return [letter + str(number) for letter in "ABCDEFGHIJ"[:rows] for number in range(1, cols + 1)]
test_main.py
from main import seat_labels
def test_two_rows():
"""Two rows of three seats"""
got = seat_labels(2, 3)
assert got == [["A1", "A2", "A3"], ["B1", "B2", "B3"]], f"seat_labels(2, 3) returned {got!r}"
def test_single_seat():
"""One row of one seat is [['A1']]"""
got = seat_labels(1, 1)
assert got == [["A1"]], f"seat_labels(1, 1) returned {got!r}, expected [['A1']]"
def test_third_row():
"""The third row starts with C"""
got = seat_labels(3, 2)
assert len(got) == 3 and got[2] == ["C1", "C2"], f"seat_labels(3, 2) returned {got!r}; the third row should be ['C1', 'C2']"
On macOS and Linux, type python3 wherever these commands say python, as in the first lesson.
Run the program:
python main.pyRun the checks (needs learnrun.py in the same folder):
python learnrun.py testDownload learnrun.pyCommon mistakes
A tuple without parentheses
pairs = [x, x * 2 for x in range(3)]
What Python prints
SyntaxError: did you forget parentheses around the comprehension target?Why, and the fix
When the expression at the front is a tuple, it must be in parentheses: [(x, x * 2) for x in range(3)]. Without them Python cannot tell where the expression ends, and its message suggests the fix.
if at the front without else
nums = [3, -1, 2]
print([x if x > 0 for x in nums])
What Python prints
SyntaxError: expected 'else' after 'if' expressionWhy, and the fix
An if before the for is a conditional expression, which must say what to produce in both cases: [x if x > 0 else 0 for x in nums]. To drop items instead, move the if to the end: [x for x in nums if x > 0].
del past the end of the list
names = ["ana", "ben"]
del names[2]
What Python prints
IndexError: list assignment index out of rangeWhy, and the fix
names has positions 0 and 1, so there is nothing at position 2. Check the length first, or use del names[-1] for the last item. A slice is forgiving: del names[2:] removes nothing and raises no error.
Python in the browser: Pyodide 314.0.7, MPL-2.0. Licence and source
Exit ticket
5 questions, no hints. Score 80% or more to complete the lesson.
Finish every activity above to unlock the exit ticket.