Advanced Python Techniques
List Comprehensions and zip()
5 min read
Write less code, get the same result
You already know how to build a list with a for loop — you did it back in Lists and List Operations. List comprehensions are Python's way of writing that same pattern — "loop over something, transform it, collect the results" — in a single, readable line.
They don't do anything a for loop can't already do. What they buy you is less code, and once you're used to reading them, more clarity about intent: "build a list from this" rather than "set up an empty list, then loop, then append."
The naive way vs. the comprehension way
Say you have a list of numbers and need a new list containing each one cubed:
sample = [10, 20, 30, 40]
cubes = []
for x in sample:
cubes.append(x**3)
print(cubes)
Output:
[1000, 8000, 27000, 64000]
Four lines to build one list. A list comprehension does it in one:
sample = [10, 20, 30, 40]
cubes = [x**3 for x in sample]
print(cubes)
Output:
[1000, 8000, 27000, 64000]
Same result. No empty list to initialize, no .append() calls — the list builds itself.
Anatomy of a list comprehension
Diagram — Anatomy of a List Comprehension
Every list comprehension has the same shape: [expression for item in iterable], optionally followed by an if condition.
- Expression — what to compute for each item (
x**3) - For clause — where the values come from (
for x in sample) - Filter (optional) — which values to include (
if x % 2 == 0)
Read it left to right, the way you'd say it out loud: "give me x**3, for every x in sample."
Filtering with if
Add a condition to only process items that pass a test. Here, only even numbers get cubed:
sample = [10, 21, 33, 40]
cubes = [x**3 for x in sample if x % 2 == 0]
print(cubes)
Output:
[1000, 64000]
21 and 33 are odd, so they're skipped entirely — they never even reach the expression. This replaces a pattern you'd otherwise write as a loop with an if check inside it:
cubes = []
for x in sample:
if x % 2 == 0:
cubes.append(x**3)
Nested loops in a comprehension
List comprehensions can include more than one for clause, which is how you flatten nested loops into a single line. Say you have a 3×3 grid and want every (row, col) coordinate pair:
matrix = []
for x in range(3):
for y in range(3):
matrix.append((x, y))
print(matrix)
Output:
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
As a comprehension, the two for clauses read left to right in the same order they were nested:
matrix = [(x, y) for x in range(3) for y in range(3)]
print(matrix)
Output:
[(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2), (2, 0), (2, 1), (2, 2)]
The outer loop (x) comes first, the inner loop (y) comes second — exactly like the nested for version, just collapsed onto one line.
zip(): walking two lists together
zip() takes two (or more) iterables and pairs them up element by element — the first item of each, then the second item of each, and so on.
countries = ["India", "America", "Australia"]
capitals = ["Delhi", "Washington DC", "Canberra"]
print(list(zip(countries, capitals)))
Output:
[('India', 'Delhi'), ('America', 'Washington DC'), ('Australia', 'Canberra')]
This is especially useful in a for loop, when you have two related lists and want to process them side by side instead of indexing into each one separately:
countries = ["India", "America", "Australia"]
capitals = ["Delhi", "Washington DC", "Canberra"]
for country, capital in zip(countries, capitals):
print(f"{capital} is the capital of {country}")
Output:
Delhi is the capital of India
Washington DC is the capital of America
Canberra is the capital of Australia
If the two lists are different lengths, zip() stops as soon as the shorter one runs out — it never raises an error over the mismatch, it just pairs up what it can.
Why bother?
- Less code — a
forloop, an empty list, and an.append()collapse into one expression - Cleaner intent — a comprehension reads as "build this list from that," not "here's a procedure that happens to build a list"
- Still one Python object — a list comprehension produces a normal list; everything you know about lists (indexing, slicing,
len()) still applies
Comprehensions aren't always the right call — if the transformation takes several steps or is hard to read on one line, a regular for loop is clearer. Readability still wins over cleverness.
Key takeaway
A list comprehension — [expression for item in iterable if condition] — is a compact way to build a list from an existing iterable, with an optional filter to skip items you don't want. zip() pairs up two lists element by element, which is especially handy for looping over related lists together.
What's next?
Next up: Exception Handling — what happens when your code hits an error at runtime, and how to catch it gracefully instead of letting your program crash.