Start with the study notes on Comprehensions (definitions, examples and a quick revision), then work through 30 practice questions, each with the correct answer and a clear explanation of why it is right. When you are ready, take this topic as an interactive quiz to test yourself.
6 easy · 15 medium · 9 hard · Part of Python: Master Python from basics to advanced - 20 topics.
Topic overview
Comprehensions – building collections in one line
1. List comprehensions
A list comprehension builds a new list from an iterable in one line: [expression for item in iterable]. It replaces the “empty list, loop, append” pattern.
squares = []
for x in range(5):
squares.append(x ** 2)
print(squares)
print([x ** 2 for x in range(5)]) # same result
Output[0, 1, 4, 9, 16]
[0, 1, 4, 9, 16]
Example:[w.upper() for w in words], [len(w) for w in words] and [int(s) for s in strings] transform every item.
2. Filtering with if
An if at the end keeps only the items that pass the test.
print([x for x in range(10) if x % 3 == 0])
print([c for c in "hello" if c in "aeiou"])
marks = {"Ana": 92, "Ben": 45}
print([name for name, m in marks.items() if m >= 50])
Output[0, 3, 6, 9]
['e', 'o']
['Ana']
3. if-else goes at the front
To change every item (not drop any), put a conditional expression before the for.
nums = [3, -1, 5]
print([x if x > 0 else 0 for x in nums]) # transform: keeps all
print([x for x in nums if x > 0]) # filter: drops some
Output[3, 0, 5]
[3, 5]
4. Dictionary and set comprehensions
Dict comprehension:{key: value for ...}.
Set comprehension:{value for ...}, which removes duplicates.
print({x: x ** 2 for x in range(1, 4)})
d = {"a": 1, "b": 2}
print({v: k for k, v in d.items()}) # swap keys and values
print(sorted({c for c in "banana"}))
What is the output of: print([x * 2 for x in range(3)])
print([x * 2 for x in range(3)]) # [0, 2, 4]
A [0, 2, 4]
B [2, 4, 6]
C [0, 1, 2]
D [0, 2, 4, 6]
Answer: A — [0, 2, 4]
A list comprehension applies the expression (x * 2) to every value of x in range(3) = 0, 1, 2 -> [0, 2, 4].
List comprehensionsrange()
Q2.easy
Which ordinary loop is equivalent to: squares = [x**2 for x in nums]
# comprehension:
squares = [x**2 for x in nums]
# equivalent loop:
squares = []
for x in nums:
squares.append(x**2)
A squares = []\nfor x in nums:\n squares.append(x**2)
B for x in nums: squares = x**2
C squares = for x in nums: x**2
D while nums: squares.append(x**2)
Answer: A — squares = []\nfor x in nums:\n squares.append(x**2)
A comprehension is exactly a build-a-list loop compressed to one line: create empty list, loop, append the expression. Same result, less code.
append()Patterns
Q5.easy
What is the output of: print([w.upper() for w in ["hi", "bye"]])
words = ["hi", "bye"]
print([w.upper() for w in words]) # ['HI', 'BYE']
A ['HI', 'BYE']
B ['hi', 'bye']
C 'HI BYE'
D Error
Answer: A — ['HI', 'BYE']
The expression can call any method: each word is uppercased -> ['HI', 'BYE']. Comprehensions shine for transforming every item.
String methodsTransformation
Q17.easy
What is the main reason to use a comprehension instead of a loop?
A It's the only way to build lists
B Shorter, clearer 'build a list from a list' code (and often slightly faster)
C It uses no memory at all
D Loops are deprecated
Answer: B — Shorter, clearer 'build a list from a list' code (and often slightly faster)
For simple transform/filter operations, one readable line beats four. But if the logic needs multiple steps or side effects, a normal loop is clearer - don't force everything into a comprehension.
Pythonic styleReadability
Q26.easy
What is the output of: print([len(x) for x in ["a", "bb", "ccc"]])
A [1, 2, 3]
B [3, 2, 1]
C 6
D ['a', 'bb', 'ccc']
Answer: A — [1, 2, 3]
len() of each string: 1, 2, 3. A straightforward mapping comprehension.
len()Mapping
Q28.easy
What is the output of: print([x for x in "abc"])
A ['abc']
B ['a', 'b', 'c']
C 'abc'
D Error
Answer: B — ['a', 'b', 'c']
Iterating a string yields characters, and the comprehension collects them: ['a', 'b', 'c'] - same result as list("abc").
Strings as iterableslist()
Medium questions (15)
Q3.medium
What is the output of: print([x for x in range(10) if x % 3 == 0])
print([x for x in range(10) if x % 3 == 0])
# [0, 3, 6, 9]
A [3, 6, 9]
B [0, 3, 6, 9]
C [1, 2, 4, 5, 7, 8]
D [0, 3, 6]
Answer: B — [0, 3, 6, 9]
The if clause FILTERS: only x values divisible by 3 are kept - 0, 3, 6, 9. Don't forget 0 is divisible by 3!
FilteringModulo
Q6.medium
What does this build?
lengths = [len(w) for w in ["apple", "fig", "banana"]]
A [5, 3, 6]
B ['apple', 'fig', 'banana']
C 14
D [6, 3, 5]
Answer: A — [5, 3, 6]
len() runs on each word: 'apple' -> 5, 'fig' -> 3, 'banana' -> 6, giving [5, 3, 6] in the same order.
len()Mapping
Q8.medium
Which comprehension creates a DICT of squares: {1: 1, 2: 4, 3: 9}?
squares = {x: x**2 for x in range(1, 4)}
print(squares) # {1: 1, 2: 4, 3: 9}
A {x: x**2 for x in range(1, 4)}
B [x: x**2 for x in range(1, 4)]
C {x, x**2 for x in range(1, 4)}
D dict(x**2 for x in range(1, 4))
Answer: A — {x: x**2 for x in range(1, 4)}
Dict comprehensions use braces with a key: value expression. {x: x**2 ...} maps each number to its square.
Dict comprehensions
Q9.medium
What does this produce?
{c for c in "banana"}
print({c for c in "banana"}) # {'b', 'a', 'n'}
A A set: {'b', 'a', 'n'} - unique characters only
B A list of 6 characters
C A dict
D The string 'ban'
Answer: A — A set: {'b', 'a', 'n'} - unique characters only
Braces without a colon make a SET comprehension - duplicates are dropped automatically, leaving the unique characters of 'banana'.
Set comprehensionsUniqueness
Q10.medium
What is (x**2 for x in range(3)) - with PARENTHESES?
gen = (x**2 for x in range(3))
print(gen) # <generator object ...>
print(list(gen)) # [0, 1, 4]
A A tuple comprehension
B A generator expression - values are produced lazily, one at a time
C A syntax error
D The same as a list comprehension
Answer: B — A generator expression - values are produced lazily, one at a time
Parentheses make a GENERATOR expression: nothing is computed until you iterate it, and values are produced one by one - using almost no memory. There is no 'tuple comprehension'; use tuple(...) to get a tuple.
GeneratorsLazy evaluation
Q12.medium
What is the output of:
print([c for c in "hello" if c in "aeiou"])
vowels = [c for c in "hello" if c in "aeiou"]
print(vowels) # ['e', 'o']
print("".join(vowels)) # eo
A ['e', 'o']
B ['h', 'l', 'l']
C 'eo'
D ['a', 'e', 'i', 'o', 'u']
Answer: A — ['e', 'o']
Loop over each character, keep only vowels: 'e' and 'o' -> ['e', 'o']. Use "".join(...) if you want them as one string.
Filteringjoin()
Q15.medium
Which builds a list of (number, square) PAIRS: [(1, 1), (2, 4), (3, 9)]?
pairs = [(x, x**2) for x in range(1, 4)]
print(pairs) # [(1, 1), (2, 4), (3, 9)]
A [(x, x**2) for x in range(1, 4)]
B [x, x**2 for x in range(1, 4)]
C {x: x**2 for x in range(1, 4)}
D [(x)(x**2) for x in range(1, 4)]
Answer: A — [(x, x**2) for x in range(1, 4)]
Wrap the pair in parentheses to make a tuple per item: (x, x**2). Without the inner parentheses (option B) it's a SyntaxError.
TuplesPairs
Q16.medium
How do you swap keys and values of d = {"a": 1, "b": 2} with a comprehension?
d = {"a": 1, "b": 2}
flipped = {v: k for k, v in d.items()}
print(flipped) # {1: 'a', 2: 'b'}
A {v: k for k, v in d.items()}
B {k: v for v, k in d.items()}
C d.swap()
D {d[k]: k for k in d.values()}
Answer: A — {v: k for k, v in d.items()}
Loop over items() to get (key, value) pairs, then write them flipped: value becomes the key -> {1: 'a', 2: 'b'}.
Only safe when values are unique and hashable.
Dict comprehensionsitems()
Q18.medium
What does any() return here?
any(x > 10 for x in [3, 15, 7])
nums = [3, 15, 7]
print(any(x > 10 for x in nums)) # True
print(all(x > 10 for x in nums)) # False
A True - at least one value passes the test
B False
C [False, True, False]
D 15
Answer: A — True - at least one value passes the test
any() is True if AT LEAST ONE element of the iterable is truthy - 15 > 10 passes, so True. It even stops early at the first success.
any()all()Generators
Q19.medium
What does all() return here?
all(x > 0 for x in [1, 2, 3])
print(all(x > 0 for x in [1, 2, 3])) # True
print(all(x > 0 for x in [1, -2, 3])) # False
A False
B True - every element passes
C 3
D [True, True, True]
Answer: B — True - every element passes
all() is True only when EVERY element is truthy - all three numbers are positive, so True. One failure would make it False (and it stops checking there).
all()any()
Q21.medium
What is the output of:
print(sorted([3, 1, 2], reverse=True))
A [1, 2, 3]
B [3, 2, 1]
C [2, 1, 3]
D None
Answer: B — [3, 2, 1]
sorted() with reverse=True sorts descending: [3, 2, 1]. Comprehensions often feed into sorted(): sorted([w.lower() for w in words]).
sorted()reverse parameter
Q23.medium
Convert with a comprehension: strings = ["1", "2", "3"] into numbers [1, 2, 3]
strings = ["1", "2", "3"]
nums = [int(s) for s in strings]
print(sum(nums)) # 6
A [int(s) for s in strings]
B int(strings)
C [s.int() for s in strings]
D strings.to_int()
Answer: A — [int(s) for s in strings]
Apply int() to each element: [int(s) for s in strings]. int(strings) fails - int() can't convert a whole list at once.
Type conversionmap()
Q25.medium
What is the output of:
print([i for i in range(20) if i % 2 == 0 if i % 3 == 0])
[i for i in range(20) if i % 2 == 0 if i % 3 == 0]
# same as: if i % 6 == 0
A [0, 6, 12, 18]
B [6, 12, 18]
C [0, 2, 3, 4, 6...]
D SyntaxError
Answer: A — [0, 6, 12, 18]
Multiple if clauses act like AND - keep numbers divisible by 2 AND by 3 (i.e. by 6): 0, 6, 12, 18. Equivalent to one if with and.
Multiple filters
Q27.medium
Marks: {"Ana": 92, "Ben": 45}. Which builds a list of names who PASSED (>= 50)?
marks = {"Ana": 92, "Ben": 45}
passed = [n for n, m in marks.items() if m >= 50]
print(passed) # ['Ana']
A [name for name, mark in marks.items() if mark >= 50]
B [mark for mark in marks if mark >= 50]
C [name for name in marks.values() if name >= 50]
D marks.passed()
Answer: A — [name for name, mark in marks.items() if mark >= 50]
Iterate items() to get both name and mark, filter on the mark, collect the name: ['Ana']. Option B loops over keys (strings) and compares them to 50 - TypeError.
items()Filtering dicts
Q30.medium
Clean this data with one comprehension: names = [" ana ", "BEN ", " Cy"] -> ["Ana", "Ben", "Cy"]
names = [" ana ", "BEN ", " Cy"]
clean = [n.strip().title() for n in names]
print(clean) # ['Ana', 'Ben', 'Cy']
A [n.strip().title() for n in names]
B [n.title() for n in names.strip()]
C names.strip().title()
D [strip(n) + title(n) for n in names]
Answer: A — [n.strip().title() for n in names]
Chain methods inside the expression: strip() removes the spaces, then title() fixes the capitalization - applied to each name. Option B calls strip() on the LIST, which doesn't exist.
Method chainingData cleaning
Hard questions (9)
Q4.hard
What is the output of: print([x * 2 for x in range(4) if x % 2 == 0])
# x in 0,1,2,3 -> keep 0,2 -> double -> [0, 4]
A [0, 2, 4, 6]
B [0, 4]
C [2, 6]
D [0, 2]
Answer: B — [0, 4]
First the filter keeps even x (0 and 2), then the expression doubles them -> [0, 4]. Filter first, transform second.
Filter + transform
Q7.hard
Where does if-ELSE go in a comprehension?
result = [x if x > 0 else 0 for x in nums]
nums = [3, -1, 5]
print([x if x > 0 else 0 for x in nums]) # [3, 0, 5] - transform
print([x for x in nums if x > 0]) # [3, 5] - filter
A BEFORE the for - because with else it's a conditional EXPRESSION, not a filter
B After the for, like a filter
C if-else is not allowed in comprehensions
D Either position works
Answer: A — BEFORE the for - because with else it's a conditional EXPRESSION, not a filter
Two different ifs! A filtering if (no else) goes AFTER the for and drops items. A conditional expression (with else) goes BEFORE the for and transforms every item. [x if x > 0 else 0 ...] keeps all items, replacing negatives with 0.
[x for x in nums if x > 0 else 0] is a SyntaxError.
Ternary expressionFiltering
Q11.hard
Why prefer sum(x*x for x in range(10**7)) over sum([x*x for x in range(10**7)])?
total = sum(x*x for x in range(10**7)) # memory-friendly
A The first is shorter to type only
B The generator version never builds a 10-million-item list in memory - values stream one by one
C The list version gives a wrong answer
D There is no difference
Answer: B — The generator version never builds a 10-million-item list in memory - values stream one by one
The list comprehension materializes ALL 10 million squares in RAM before summing. The generator produces each value, hands it to sum(), and forgets it - constant memory. For large data, generators win.
GeneratorsMemory efficiency
Q13.hard
What is the output of:
print([x + y for x in "ab" for y in "cd"])
# same as:
for x in "ab":
for y in "cd":
result.append(x + y)
A ['ac', 'ad', 'bc', 'bd']
B ['ac', 'bd']
C ['ab', 'cd']
D ['ca', 'da', 'cb', 'db']
Answer: A — ['ac', 'ad', 'bc', 'bd']
Two for clauses nest like a double loop - the FIRST for is the outer loop. For each x ('a', then 'b'), y runs through 'c', 'd': ac, ad, bc, bd.
Nested comprehensionsCartesian product
Q14.hard
How do you FLATTEN matrix = [[1, 2], [3, 4]] into [1, 2, 3, 4] with a comprehension?
matrix = [[1, 2], [3, 4]]
flat = [x for row in matrix for x in row]
print(flat) # [1, 2, 3, 4]
A [x for row in matrix for x in row]
B [x for x in row for row in matrix]
C [matrix for row in x]
D flatten(matrix)
Answer: A — [x for row in matrix for x in row]
Read the for clauses LEFT TO RIGHT like nested loops: first 'for row in matrix' (outer), then 'for x in row' (inner). Option B is backwards - row isn't defined yet.
Order rule: the fors appear in the same order as in the loop version.
FlatteningNested loops
Q20.hard
Which correctly builds a 3x3 grid of zeros where rows are INDEPENDENT?
bad = [[0] * 3] * 3
bad[0][0] = 9
print(bad) # [[9,0,0],[9,0,0],[9,0,0]] !!
good = [[0] * 3 for _ in range(3)]
good[0][0] = 9
print(good) # [[9,0,0],[0,0,0],[0,0,0]]
A grid = [[0] * 3 for _ in range(3)]
B grid = [[0] * 3] * 3
C Both A and B are identical
D grid = [0] * 9
Answer: A — grid = [[0] * 3 for _ in range(3)]
Option B repeats a reference to the SAME row three times - changing grid[0][0] would change all three rows! The comprehension creates a fresh list each iteration, so rows are independent.
A classic trap when building 2D grids for games or matrices.
References2D lists
Q22.hard
In Python 3, does the loop variable of a comprehension LEAK outside?
[x for x in range(3)]
print(x)
[x for x in range(3)]
# print(x) # NameError in Python 3
A Yes - x is 2 afterwards
B No - NameError: comprehensions have their own scope in Python 3
C x becomes the whole list
D x is always 0
Answer: B — No - NameError: comprehensions have their own scope in Python 3
In Python 3, comprehension variables live in their own hidden scope and vanish afterwards - print(x) raises NameError (unless x existed before). In Python 2 they leaked; this was fixed.
ScopePython 2 vs 3
Q24.hard
Which is equivalent to map(str.upper, words)?
words = ["hi", "bye"]
print(list(map(str.upper, words))) # ['HI', 'BYE']
print([w.upper() for w in words]) # same
A [str.upper for w in words]
B [w.upper() for w in words]
C {w.upper() for w in words}
D (words.upper())
Answer: B — [w.upper() for w in words]
map(function, iterable) applies the function to each item - exactly what [w.upper() for w in words] does. Most Python style guides prefer the comprehension for readability.
map()filter()
Q29.hard
What is the output?
funcs = [x**2 for x in range(3)]
print(funcs[-1])
A 4
B 2
C 9
D [0, 1, 4]
Answer: A — 4
The comprehension builds [0, 1, 4] (squares of 0, 1, 2), and [-1] takes the last element: 4. Combining comprehension results with indexing is common in interview snippets.