Learning Goals
3 minBy the end of this lesson you can:
- Recognise a list comprehension —
[expr for item in iterable]— and read it out loud. - Rewrite a simple
for-loop that builds a list as a one-line comprehension. - Choose between a loop and a comprehension and explain why.
Warm-Up
5 minIn Level 1 we wrote a lot of loops that look exactly like this — start with an empty list, walk through some other list, append something to the new one:
numbers = [1, 2, 3, 4, 5] doubles = [] for n in numbers: doubles.append(n * 2) print(doubles)
Four lines, eighteen years old as a pattern, and still everywhere. What if we could collapse the empty list, the loop and the append into one line?
A list comprehension is just a tidier way to write the loop you just saw. Same result, one line, easier to read once your eyes know what to look for.
New Concept · The Comprehension Pattern
12 minThe shape
Every list comprehension fits this template:
[ EXPRESSION for ITEM in ITERABLE ]
Read it left to right as English:
- The new item I want
- for each
- item
- in
- this collection
Side by side
Watch the four-liner shrink to one:
The loop way
numbers = [1, 2, 3, 4, 5] doubles = [] for n in numbers: doubles.append(n * 2) print(doubles) # → [2, 4, 6, 8, 10]
The comprehension way
numbers = [1, 2, 3, 4, 5] doubles = [n * 2 for n in numbers] print(doubles) # → [2, 4, 6, 8, 10]
Same answer, same speed, fewer typos. The empty list, the loop and the .append() all became the square-brackets [ ... for ... in ... ].
More tiny examples
Upper-case every name
names = ["aisyah", "wei jie", "priya"] shouty = [name.upper() for name in names] print(shouty) # → ['AISYAH', 'WEI JIE', 'PRIYA']
The squares of 1 to 5
squares = [n ** 2 for n in range(1, 6)] print(squares) # → [1, 4, 9, 16, 25]
Length of each word
words = ["roti", "nasi lemak", "satay"] lengths = [len(w) for w in words] print(lengths) # → [4, 10, 5]
If your loop does more than one thing — prints, modifies another variable, has an if/else that branches the work — keep it as a regular for loop. Comprehensions are for the one job of building a new list. Anything else and they become hard to read.
A note on filters
Comprehensions can include an if at the end — for example [n for n in nums if n > 0]. We'll see filters and if/else inside comprehensions in PY-L3-21. For today, master the plain [expr for item in iterable] shape first.
Worked Example · From Loops to One-Liners
12 minWe'll convert three real Level-1 loops into comprehensions. Type each pair and run them to confirm they give the same answer.
Example 1 · Prices with VAT
Old way — add 6% SST to every menu price.
Loop
prices = [5.50, 3.00, 8.00] with_sst = [] for p in prices: with_sst.append(p * 1.06) print(with_sst)
Comprehension
prices = [5.50, 3.00, 8.00] with_sst = [p * 1.06 for p in prices] print(with_sst)
[5.83, 3.18, 8.48]
Example 2 · First letters
Build a list of just the first letter of each name.
Loop
names = ["Aisyah", "Wei Jie", "Priya"] initials = [] for n in names: initials.append(n[0]) print(initials)
Comprehension
names = ["Aisyah", "Wei Jie", "Priya"] initials = [n[0] for n in names] print(initials)
['A', 'W', 'P']
Example 3 · A times-table row
The seven times table from 1 to 12.
Loop
row = [] for n in range(1, 13): row.append(n * 7) print(row)
Comprehension
row = [n * 7 for n in range(1, 13)] print(row)
[7, 14, 21, 28, 35, 42, 49, 56, 63, 70, 77, 84]
When you see [n * 7 for n in range(1, 13)], read it backwards — "for n in range one to thirteen, give me n times seven". The right-hand side feeds the left-hand side.
Try It Yourself
13 minThree exercises. Write each one twice — first as a regular loop, then as a comprehension. Confirm both versions print the same answer.
Given nums = [1, 2, 3, 4, 5, 6], build a new list tripled with every number times 3.
Hint
nums = [1, 2, 3, 4, 5, 6] tripled = [n * 3 for n in nums] print(tripled) # → [3, 6, 9, 12, 15, 18]
From names = ["AISYAH", "WEI JIE", "PRIYA"], build quiet — the same names in lower case.
Hint
names = ["AISYAH", "WEI JIE", "PRIYA"] quiet = [n.lower() for n in names] print(quiet) # → ['aisyah', 'wei jie', 'priya']
Strings have a .lower() method we met in PY-L1-23. It returns a brand-new lower-cased string — perfect for the expression on the left.
Build a list of the cubes 1, 8, 27, 64 ... for the numbers 1 to 10. Use range.
Hint
cubes = [n ** 3 for n in range(1, 11)] print(cubes) # → [1, 8, 27, 64, 125, 216, 343, 512, 729, 1000]
range(1, 11) stops before 11 — so it gives the ten numbers 1, 2, 3, ..., 10. The ** operator raises to a power.
Mini-Challenge · The Menu Converter
8 minYou're given a list of dishes and a separate list of their RM prices. Build menu.py that prints a tidy menu — one line per dish.
dishes = ["nasi lemak", "char kway teow", "roti canai", "cendol"] prices = [8.00, 9.50, 3.50, 4.00]
Your file must:
- Use a list comprehension to build
shouty_dishes— every dish in UPPER CASE. - Use a list comprehension to build
rm_prices— every price as a string withRMin front, like"RM 8.0". (Usestr(p)to convert the number, and concatenation with+.) - Loop over the four positions with
range(4)and printshouty_dishes[i]andrm_prices[i]side by side.
Stretch goal. Build a third comprehension name_lengths with the length of each original dish name, then print the longest dish name (use max).
Show one possible solution
# menu.py — list comprehensions in three different shapes dishes = ["nasi lemak", "char kway teow", "roti canai", "cendol"] prices = [8.00, 9.50, 3.50, 4.00] shouty_dishes = [d.upper() for d in dishes] rm_prices = ["RM " + str(p) for p in prices] for i in range(4): print(shouty_dishes[i], "-", rm_prices[i]) # Stretch name_lengths = [len(d) for d in dishes] print() print("Longest name:", max(name_lengths), "characters")
The non-negotiables are two list comprehensions — one to upper-case the dishes, one to add "RM" to the prices — and the range(4) loop that uses both new lists by index. We'll see a much nicer way to pair two lists (zip) in Level 3.
Recap
3 minA list comprehension is the squashed-down version of a four-line loop that builds a new list. The shape [expr for item in iterable] reads left to right as "what I want, for each item, in this collection". If your loop does more than build one list — printing, branching, modifying — keep it as a regular for. We'll deep-dive comprehensions (with filters and if/else) in PY-L3-21.
Vocabulary Card
- list comprehension
- A one-line way to build a new list from another iterable. Shape:
[expr for item in iterable]. - expression
- The part on the left of
for— what each new list item should look like. - iterable
- Anything you can walk through with a
forloop — a list, a string, arange().
Homework
4 minSave a new file convert.py. In it, write each task as both a for loop and a list comprehension, and print both results. The two should always match.
- Halve every number in
[10, 20, 30, 40]. - Add the suffix
" bin Ahmad"to every name in["Iman", "Aiman", "Aizat"]. - Build a list of the even numbers between
1and20— userange(2, 21, 2)(step2), so no filter is needed.
Stretch. For task 1, after building the comprehension, use sum() to print the total of the halved numbers.
Sample · convert.py
# convert.py — three loops, three comprehensions, same answers # Task 1 — halve every number nums = [10, 20, 30, 40] halved_loop = [] for n in nums: halved_loop.append(n / 2) halved_comp = [n / 2 for n in nums] print("Task 1 loop:", halved_loop) print("Task 1 comp:", halved_comp) print("Total :", sum(halved_comp)) # Task 2 — add a suffix names = ["Iman", "Aiman", "Aizat"] full_loop = [] for n in names: full_loop.append(n + " bin Ahmad") full_comp = [n + " bin Ahmad" for n in names] print("Task 2 loop:", full_loop) print("Task 2 comp:", full_comp) # Task 3 — even numbers 2..20 using range step evens_loop = [] for n in range(2, 21, 2): evens_loop.append(n) evens_comp = [n for n in range(2, 21, 2)] print("Task 3 loop:", evens_loop) print("Task 3 comp:", evens_comp)
Each pair should produce identical output. If your loop and comprehension disagree, one of them has a typo — that side-by-side check is a great way to catch bugs.