Practice Questions — Comprehensions and Generators
← Back to NotesTopic-Specific Questions
Question 1
Easy
What is the output of the following code?
result = [x * 2 for x in [1, 2, 3, 4]]
print(result)Each element is multiplied by 2.
[2, 4, 6, 8]Question 2
Easy
What is the output?
evens = [x for x in range(10) if x % 2 == 0]
print(evens)The if clause filters elements. Only even numbers pass the condition.
[0, 2, 4, 6, 8]Question 3
Easy
What is the output?
words = ["hello", "world"]
upper = [w.upper() for w in words]
print(upper)The .upper() method converts a string to uppercase.
['HELLO', 'WORLD']Question 4
Easy
What is the output?
result = ["even" if x % 2 == 0 else "odd" for x in range(4)]
print(result)The if-else before 'for' transforms every element.
['even', 'odd', 'even', 'odd']Question 5
Easy
What is the output?
squares = {x: x**2 for x in range(4)}
print(squares)Curly braces with key:value create a dictionary comprehension.
{0: 0, 1: 1, 2: 4, 3: 9}Question 6
Medium
What is the output?
matrix = [[1, 2], [3, 4], [5, 6]]
flat = [n for row in matrix for n in row]
print(flat)Nested comprehension reads left to right: outer loop first, inner loop second.
[1, 2, 3, 4, 5, 6]Question 7
Medium
What is the output?
names = ["Aarav", "Ananya", "Priya", "Arjun"]
result = {name[0] for name in names}
print(sorted(result))Curly braces without key:value create a set comprehension. Sets have unique elements.
['A', 'P']Question 8
Medium
What is the output?
def gen():
yield 1
yield 2
yield 3
g = gen()
print(next(g))
print(next(g))
print(list(g))next() consumes one value. list() consumes all remaining values.
12[3]Question 9
Medium
What is the output?
gen = (x ** 2 for x in range(4))
print(type(gen).__name__)
print(sum(gen))
print(sum(gen))A generator can only be consumed once.
generator140Question 10
Medium
What is the output?
scores = {"Aarav": 85, "Priya": 42, "Rohan": 91}
result = {k: v for k, v in scores.items() if v >= 50}
print(result)The if clause filters dictionary items by value.
{'Aarav': 85, 'Rohan': 91}Question 11
Medium
What is the output?
def countdown(n):
while n > 0:
yield n
n -= 1
nums = list(countdown(5))
print(nums)
print(sum(countdown(3)))Each call to countdown() creates a fresh generator.
[5, 4, 3, 2, 1]6Question 12
Hard
What is the output?
result = [[i*j for j in range(1, 4)] for i in range(1, 4)]
for row in result:
print(row)Outer comprehension creates rows (i=1,2,3). Inner comprehension creates columns (j=1,2,3).
[1, 2, 3][2, 4, 6][3, 6, 9]Question 13
Hard
What is the output?
def gen_ab():
yield "a"
yield "b"
def gen_cd():
yield "c"
yield "d"
def chain(*generators):
for gen in generators:
yield from gen
result = list(chain(gen_ab(), gen_cd()))
print(result)yield from delegates to another generator, yielding all its values.
['a', 'b', 'c', 'd']Question 14
Hard
What is the output?
original = [[1, 2, 3], [4, 5, 6]]
transposed = [[row[i] for row in original] for i in range(3)]
print(transposed)The outer comprehension iterates column indices. The inner collects that column from each row.
[[1, 4], [2, 5], [3, 6]]Question 15
Hard
What is the output?
def infinite():
n = 0
while True:
yield n
n += 1
gen = infinite()
result = [next(gen) for _ in range(5)]
next(gen) # Skip one
result.append(next(gen))
print(result)next(gen) advances the generator by one. The skipped value is not in result.
[0, 1, 2, 3, 4, 6]Question 16
Hard
What is the output?
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# Three different comprehension types
list_result = [x for x in data if x % 3 == 0]
dict_result = {x: x**2 for x in data if x % 3 == 0}
set_result = {x % 5 for x in data if x % 3 == 0}
print(list_result)
print(dict_result)
print(sorted(set_result))Multiples of 3 in 1-10 are 3, 6, 9.
[3, 6, 9]{3: 9, 6: 36, 9: 81}[0, 1, 4]Question 17
Medium
What is the difference between
[x for x in items if x > 0] and [x if x > 0 else 0 for x in items]?One filters, the other transforms.
The first expression filters: it includes only elements where x > 0. Elements that fail the condition are excluded entirely, so the result may have fewer elements. The second expression transforms: it includes every element, but replaces non-positive ones with 0. The result always has the same number of elements as the input.
Question 18
Hard
Why are generators described as 'lazy'? What advantage does lazy evaluation provide?
Think about when values are computed and how much memory is used.
Generators are 'lazy' because they compute each value only when it is requested (by
next() or a for loop), not in advance. This provides two advantages: (1) Memory efficiency -- only one value exists in memory at a time, regardless of the total number of values. A generator over 10 million items uses the same memory as one over 10 items. (2) Computation efficiency -- if you only need the first few values (e.g., finding the first match), the remaining values are never computed at all.Mixed & Application Questions
Question 1
Easy
What is the output?
lengths = [len(w) for w in ["hi", "hello", "hey"]]
print(lengths)len() returns the length of each string.
[2, 5, 3]Question 2
Easy
What is the output?
chars = [c for c in "Python"]
print(chars)Iterating over a string gives individual characters.
['P', 'y', 't', 'h', 'o', 'n']Question 3
Easy
What is the output?
pairs = [(x, y) for x in [1, 2] for y in ["a", "b"]]
print(pairs)Two for clauses create all combinations (Cartesian product).
[(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]Question 4
Medium
What is the output?
nums = [1, 2, 3, 4, 5, 6]
result = [x ** 2 for x in nums if x % 2 != 0]
print(result)The if clause filters to odd numbers first, then squares them.
[1, 9, 25]Question 5
Medium
What is the output?
sentence = "the quick brown fox"
word_map = {w: len(w) for w in sentence.split()}
print(word_map)split() breaks the string into words. Dict comprehension maps each word to its length.
{'the': 3, 'quick': 5, 'brown': 5, 'fox': 3}Question 6
Medium
What is the output?
def double_gen(n):
for i in range(n):
yield i * 2
result = []
for val in double_gen(4):
result.append(val)
print(result)The generator yields 0*2, 1*2, 2*2, 3*2.
[0, 2, 4, 6]Question 7
Medium
What is the output?
data = ["hello", 42, "world", 3.14, "!"]
strings_only = [x.upper() for x in data if isinstance(x, str)]
print(strings_only)isinstance(x, str) filters to strings only. Then .upper() is applied.
['HELLO', 'WORLD', '!']Question 8
Hard
What is the output?
def gen():
print("start")
yield 1
print("middle")
yield 2
print("end")
g = gen()
print("before next")
val = next(g)
print(f"got {val}")
val = next(g)
print(f"got {val}")Generator code runs lazily. It does not execute until next() is called.
before nextstartgot 1middlegot 2Question 9
Hard
What is the output?
nested = {"a": [1, 2], "b": [3, 4], "c": [5]}
flat = [(k, v) for k, vals in nested.items() for v in vals]
print(flat)Outer loop iterates keys and lists. Inner loop iterates values in each list.
[('a', 1), ('a', 2), ('b', 3), ('b', 4), ('c', 5)]Question 10
Hard
What is the output?
def squares_up_to(n):
i = 0
while i * i <= n:
yield i * i
i += 1
print(list(squares_up_to(20)))
print(list(squares_up_to(1)))The generator yields perfect squares that are <= n.
[0, 1, 4, 9, 16][0, 1]Question 11
Hard
What is the output?
mapping = list(map(lambda x: x**2, [1, 2, 3]))
comping = [x**2 for x in [1, 2, 3]]
genning = list(x**2 for x in [1, 2, 3])
print(mapping == comping == genning)
print(type(map(lambda x: x, [])).__name__)
print(type(x**2 for x in []).__name__)All three produce the same values. map and generator expressions are lazy.
TruemapgeneratorQuestion 12
Medium
When should you use a generator expression instead of a list comprehension?
Think about memory usage and how many times you need the data.
Use a generator expression when: (1) you only need to iterate over the values once (e.g., passing to
sum(), max(), or a for loop), (2) the data set is very large and would consume too much memory as a list, or (3) you are chaining operations where intermediate lists would be wasteful. Use a list comprehension when you need to access elements by index, iterate multiple times, or the data set is small enough that memory is not a concern.Multiple Choice Questions
MCQ 1
What does [x * 2 for x in range(3)] produce?
Answer: A
A is correct. range(3) produces 0, 1, 2. Multiplying each by 2: 0, 2, 4. Result: [0, 2, 4]. Option B would be correct for range(1, 4).
A is correct. range(3) produces 0, 1, 2. Multiplying each by 2: 0, 2, 4. Result: [0, 2, 4]. Option B would be correct for range(1, 4).
MCQ 2
What type of comprehension uses curly braces with key:value pairs?
Answer: C
C is correct.
C is correct.
{key: value for ...} creates a dictionary. {value for ...} (no colon) creates a set. [value for ...] creates a list. There is no tuple comprehension syntax.MCQ 3
What keyword is used in a generator function to produce values?
Answer: B
B is correct.
B is correct.
yield is the keyword that makes a function a generator. It pauses the function and produces a value. return would end the function entirely.MCQ 4
What is the output of (x for x in [1, 2, 3])?
Answer: C
C is correct. Parentheses with a for expression create a generator object, not a tuple or list. To get a list, use
C is correct. Parentheses with a for expression create a generator object, not a tuple or list. To get a list, use
list(x for x in [1, 2, 3]).MCQ 5
Which comprehension syntax filters elements?
Answer: B
B is correct. The filter condition goes after the for clause:
B is correct. The filter condition goes after the for clause:
[x for x in items if condition]. Option A is a SyntaxError (if-else requires an else). Options C and D are not valid Python syntax.MCQ 6
What happens when you call next() on an exhausted generator?
Answer: C
C is correct. When a generator has no more values to yield, calling
C is correct. When a generator has no more values to yield, calling
next() raises StopIteration. A for loop catches this automatically, but manual next() calls will see it.MCQ 7
In [x if x > 0 else -x for x in numbers], where does the if-else go?
Answer: B
B is correct. The
B is correct. The
if-else expression (ternary operator) goes before the for clause when you want to transform every element. The if goes after the for clause only when you want to filter elements.MCQ 8
What is lazy evaluation?
Answer: B
B is correct. Lazy evaluation means values are computed on demand, not in advance. Generators use lazy evaluation -- each value is computed only when
B is correct. Lazy evaluation means values are computed on demand, not in advance. Generators use lazy evaluation -- each value is computed only when
next() is called or when a for loop requests it.MCQ 9
How much memory does a generator expression use compared to a list comprehension for 1 million elements?
Answer: C
C is correct. A generator object is about 200 bytes regardless of how many values it will produce. A list comprehension for 1 million integers uses about 8 MB. The generator computes values one at a time, never storing the full sequence.
C is correct. A generator object is about 200 bytes regardless of how many values it will produce. A list comprehension for 1 million integers uses about 8 MB. The generator computes values one at a time, never storing the full sequence.
MCQ 10
What does {x % 3 for x in range(10)} create?
Answer: C
C is correct. Curly braces without key:value pairs create a set. The remainders when dividing 0-9 by 3 are 0, 1, 2, 0, 1, 2, 0, 1, 2, 0. The set contains only unique values: {0, 1, 2}.
C is correct. Curly braces without key:value pairs create a set. The remainders when dividing 0-9 by 3 are 0, 1, 2, 0, 1, 2, 0, 1, 2, 0. The set contains only unique values: {0, 1, 2}.
MCQ 11
What is the difference between yield and return?
Answer: C
C is correct.
C is correct.
return terminates the function permanently and sends back one value. yield pauses the function, sends back a value, and preserves all local variables. When next() is called again, execution resumes from where it paused.MCQ 12
What is the output of [n for row in [[1,2],[3]] for n in row]?
Answer: B
B is correct. The outer loop iterates over rows: [1,2] then [3]. The inner loop iterates numbers in each row: 1, 2, then 3. All numbers are collected into a flat list: [1, 2, 3]. This is the flattening pattern.
B is correct. The outer loop iterates over rows: [1,2] then [3]. The inner loop iterates numbers in each row: 1, 2, then 3. All numbers are collected into a flat list: [1, 2, 3]. This is the flattening pattern.
MCQ 13
Can a generator function have both yield and return statements?
Answer: C
C is correct. A generator function can have
C is correct. A generator function can have
return (with or without a value). A bare return or falling off the end of the function raises StopIteration. return value raises StopIteration(value), where the value can be accessed from the exception but is not yielded.MCQ 14
Why is a list comprehension typically faster than an equivalent for-loop-with-append?
Answer: C
C is correct. List comprehensions are compiled to optimized bytecode. They avoid the overhead of calling
C is correct. List comprehensions are compiled to optimized bytecode. They avoid the overhead of calling
list.append() on each iteration (which involves attribute lookup and function call). The improvement is typically 10-30% for simple operations.MCQ 15
What does yield from iterable do in a generator function?
Answer: B
B is correct.
B is correct.
yield from iterable delegates to a sub-iterator, yielding each of its values one at a time. It is equivalent to for item in iterable: yield item but more efficient and also supports send/throw for advanced generator protocols.MCQ 16
What is the result of sum(x for x in range(4))?
Answer: B
B is correct. The generator expression produces 0, 1, 2, 3 (range(4) goes from 0 to 3).
B is correct. The generator expression produces 0, 1, 2, 3 (range(4) goes from 0 to 3).
sum() adds them: 0+1+2+3 = 6. Generator expressions can be passed directly to functions like sum(), max(), min(), any(), all().MCQ 17
What does [x.strip() for x in [' hi ', ' bye ']] produce?
Answer: A
A is correct. The
A is correct. The
.strip() method removes leading and trailing whitespace from each string. ' hi ' becomes 'hi' and ' bye ' becomes 'bye'.MCQ 18
Can you iterate over a generator with a for loop?
Answer: B
B is correct. A for loop works with any iterable, including generators. Internally, it calls
B is correct. A for loop works with any iterable, including generators. Internally, it calls
iter() (which returns the generator itself) and then next() repeatedly until StopIteration is raised. The for loop handles StopIteration automatically.MCQ 19
What is the correct way to create a tuple from a comprehension-like syntax?
Answer: C
C is correct. There is no tuple comprehension syntax. Option A creates a generator, not a tuple. Option C wraps a generator expression with
C is correct. There is no tuple comprehension syntax. Option A creates a generator, not a tuple. Option C wraps a generator expression with
tuple() to create a tuple. Options B and D are not valid Python syntax.MCQ 20
What is the advantage of chaining generators over using intermediate lists?
Answer: C
C is correct. When generators are chained, each element flows through the entire pipeline one at a time. No intermediate lists are created, so memory usage is constant regardless of data size. This is not parallel processing (option B), and it is not necessarily faster for small data (option D).
C is correct. When generators are chained, each element flows through the entire pipeline one at a time. No intermediate lists are created, so memory usage is constant regardless of data size. This is not parallel processing (option B), and it is not necessarily faster for small data (option D).
Coding Challenges
Challenge 1: Comprehension Toolbox
EasyWrite the following using list comprehensions: (1) squares of numbers 1-10, (2) only even numbers from a list, (3) first character of each word in a sentence, (4) replace negative numbers with 0.
Sample Input
(No input required)
Sample Output
Squares: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
Evens: [2, 4, 6, 8, 10]
Initials: ['T', 'q', 'b', 'f']
Cleaned: [3, 0, 7, 0, 2]
Use only list comprehensions (no for loops).
# 1. Squares of 1-10
squares = [x ** 2 for x in range(1, 11)]
print(f"Squares: {squares}")
# 2. Even numbers
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
evens = [x for x in numbers if x % 2 == 0]
print(f"Evens: {evens}")
# 3. First character of each word
sentence = "The quick brown fox"
initials = [w[0] for w in sentence.split()]
print(f"Initials: {initials}")
# 4. Replace negatives with 0
data = [3, -1, 7, -5, 2]
cleaned = [x if x >= 0 else 0 for x in data]
print(f"Cleaned: {cleaned}")Challenge 2: Dict and Set Comprehension Practice
EasyUse comprehensions to: (1) create a dict mapping each number 1-5 to its cube, (2) invert a dictionary (swap keys and values), (3) create a set of unique word lengths from a sentence, (4) filter a dictionary to keep only entries where the value is greater than 50.
Sample Input
(No input required)
Sample Output
Cubes: {1: 1, 2: 8, 3: 27, 4: 64, 5: 125}
Inverted: {1: 'a', 2: 'b', 3: 'c'}
Lengths: {3, 4, 5}
Passed: {'Aarav': 85, 'Rohan': 91}
Use only dict/set comprehensions.
# 1. Number to cube mapping
cubes = {x: x**3 for x in range(1, 6)}
print(f"Cubes: {cubes}")
# 2. Invert a dictionary
original = {'a': 1, 'b': 2, 'c': 3}
inverted = {v: k for k, v in original.items()}
print(f"Inverted: {inverted}")
# 3. Unique word lengths
sentence = "the quick brown fox jumps"
lengths = {len(w) for w in sentence.split()}
print(f"Lengths: {sorted(lengths)}")
# 4. Filter dictionary by value
scores = {'Aarav': 85, 'Priya': 42, 'Rohan': 91, 'Meera': 38}
passed = {k: v for k, v in scores.items() if v > 50}
print(f"Passed: {passed}")Challenge 3: Fibonacci Generator
EasyWrite a generator function fibonacci() that yields Fibonacci numbers indefinitely. Then write code that uses it to: (1) get the first 10 Fibonacci numbers, (2) find the first Fibonacci number greater than 100, (3) sum all Fibonacci numbers under 50.
Sample Input
(No input required)
Sample Output
First 10: [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
First > 100: 144
Sum under 50: 88
The generator must be infinite. Use next() and for loops to consume values.
def fibonacci():
a, b = 0, 1
while True:
yield a
a, b = b, a + b
# 1. First 10 Fibonacci numbers
fib = fibonacci()
first_10 = [next(fib) for _ in range(10)]
print(f"First 10: {first_10}")
# 2. First Fibonacci number > 100
fib = fibonacci()
for num in fib:
if num > 100:
print(f"First > 100: {num}")
break
# 3. Sum of Fibonacci numbers under 50
fib = fibonacci()
total = 0
for num in fib:
if num >= 50:
break
total += num
print(f"Sum under 50: {total}")Challenge 4: Matrix Operations with Comprehensions
MediumUsing only comprehensions, write functions for: (1) create_matrix(rows, cols, fill=0) that creates a 2D list, (2) transpose(matrix) that swaps rows and columns, (3) flatten(matrix) that converts 2D to 1D, (4) matrix_add(a, b) that adds two matrices element-wise.
Sample Input
(No input required)
Sample Output
Created: [[0, 0, 0], [0, 0, 0]]
Transpose: [[1, 4], [2, 5], [3, 6]]
Flattened: [1, 2, 3, 4, 5, 6]
Sum: [[6, 8], [10, 12]]
Use comprehensions for all operations. No explicit for-loop-with-append.
def create_matrix(rows, cols, fill=0):
return [[fill for _ in range(cols)] for _ in range(rows)]
def transpose(matrix):
if not matrix:
return []
return [[row[i] for row in matrix] for i in range(len(matrix[0]))]
def flatten(matrix):
return [val for row in matrix for val in row]
def matrix_add(a, b):
return [[a[i][j] + b[i][j] for j in range(len(a[0]))] for i in range(len(a))]
print(f"Created: {create_matrix(2, 3)}")
print(f"Transpose: {transpose([[1, 2, 3], [4, 5, 6]])}")
print(f"Flattened: {flatten([[1, 2, 3], [4, 5, 6]])}")
m1 = [[1, 2], [3, 4]]
m2 = [[5, 6], [7, 8]]
print(f"Sum: {matrix_add(m1, m2)}")Challenge 5: Generator Pipeline: Text Processor
MediumBuild a text processing pipeline using chained generators: (1) read_lines(text) yields each line from a multi-line string, (2) strip_lines(lines) yields stripped lines, (3) remove_empty(lines) yields only non-empty lines, (4) to_words(lines) yields individual words from each line, (5) unique_words(words) yields each word only the first time it appears.
Sample Input
(No input required)
Sample Output
Lines: 5
Clean lines: 3
Words: ['Hello', 'world', 'Python', 'is', 'great', 'Hello', 'Python']
Unique: ['Hello', 'world', 'Python', 'is', 'great']
Each function must be a generator. Chain them together.
def read_lines(text):
for line in text.split('\n'):
yield line
def strip_lines(lines):
for line in lines:
yield line.strip()
def remove_empty(lines):
for line in lines:
if line:
yield line
def to_words(lines):
for line in lines:
for word in line.split():
yield word
def unique_words(words):
seen = set()
for word in words:
if word not in seen:
seen.add(word)
yield word
text = """ Hello world
Python is great
Hello Python """
all_lines = list(read_lines(text))
print(f"Lines: {len(all_lines)}")
clean = list(remove_empty(strip_lines(read_lines(text))))
print(f"Clean lines: {len(clean)}")
words = list(to_words(remove_empty(strip_lines(read_lines(text)))))
print(f"Words: {words}")
uniq = list(unique_words(to_words(remove_empty(strip_lines(read_lines(text))))))
print(f"Unique: {uniq}")Challenge 6: Infinite Generators Collection
MediumWrite these infinite generator functions: (1) naturals(start=1) yields natural numbers from start, (2) cycle(iterable) repeats an iterable endlessly, (3) repeat(value, times=None) yields a value forever (or times times if specified). Then write take(n, gen) to get the first n values from any generator.
Sample Input
(No input required)
Sample Output
Naturals: [1, 2, 3, 4, 5]
Cycle: ['a', 'b', 'c', 'a', 'b', 'c', 'a']
Repeat forever: [42, 42, 42, 42, 42]
Repeat 3 times: [42, 42, 42]
naturals and cycle must be truly infinite. take() must work with any generator.
def naturals(start=1):
n = start
while True:
yield n
n += 1
def cycle(iterable):
items = list(iterable)
while True:
for item in items:
yield item
def repeat(value, times=None):
if times is None:
while True:
yield value
else:
for _ in range(times):
yield value
def take(n, gen):
return [next(gen) for _ in range(n)]
print(f"Naturals: {take(5, naturals())}")
print(f"Cycle: {take(7, cycle(['a', 'b', 'c']))}")
print(f"Repeat forever: {take(5, repeat(42))}")
print(f"Repeat 3 times: {list(repeat(42, 3))}")Challenge 7: Comprehension-Based Data Analysis
HardGiven a list of student records (dicts with 'name', 'subject', 'score'), use only comprehensions to: (1) get names of students who scored above 80, (2) create a dict mapping each subject to its average score, (3) find the top scorer in each subject, (4) create a grade distribution dict (A: 90+, B: 75+, C: 50+, F: below 50).
Sample Input
(No input required)
Sample Output
Above 80: ['Aarav', 'Rohan', 'Kavya']
Averages: {'Math': 74.33, 'Python': 68.0, 'Science': 73.0}
Top per subject: {'Math': 'Rohan', 'Python': 'Kavya', 'Science': 'Aarav'}
Grade distribution: {'A': 2, 'B': 2, 'C': 1, 'F': 1}
Use comprehensions wherever possible. You may use helper comprehensions for intermediate results.
students = [
{"name": "Aarav", "subject": "Math", "score": 85},
{"name": "Priya", "subject": "Math", "score": 42},
{"name": "Rohan", "subject": "Math", "score": 96},
{"name": "Meera", "subject": "Python", "score": 55},
{"name": "Kavya", "subject": "Python", "score": 81},
{"name": "Aarav", "subject": "Science", "score": 91},
{"name": "Rohan", "subject": "Science", "score": 55},
]
# 1. Names of students who scored above 80
above_80 = [s["name"] for s in students if s["score"] > 80]
print(f"Above 80: {above_80}")
# 2. Average score per subject
subjects = {s["subject"] for s in students}
averages = {
subj: round(sum(s["score"] for s in students if s["subject"] == subj) /
sum(1 for s in students if s["subject"] == subj), 2)
for subj in subjects
}
print(f"Averages: {averages}")
# 3. Top scorer per subject
top_per_subject = {
subj: max((s for s in students if s["subject"] == subj), key=lambda s: s["score"])["name"]
for subj in subjects
}
print(f"Top per subject: {top_per_subject}")
# 4. Grade distribution
def grade(score):
if score >= 90: return "A"
if score >= 75: return "B"
if score >= 50: return "C"
return "F"
grades = [grade(s["score"]) for s in students]
distribution = {g: grades.count(g) for g in sorted(set(grades))}
print(f"Grade distribution: {distribution}")Challenge 8: Lazy File-Like Reader with Generators
HardCreate a class LazyCSV that simulates reading a CSV file lazily using generators. It should have: (1) rows() generator that yields each row as a list of strings, (2) dicts() generator that yields each row as a dictionary (using the header row as keys), (3) select(columns) generator that yields only specified columns, (4) where(column, condition) generator that filters rows based on a condition function.
Sample Input
(No input required)
Sample Output
Row: ['Aarav', '16', '85']
Dict: {'name': 'Aarav', 'age': '16', 'score': '85'}
Names: [['Aarav'], ['Priya'], ['Rohan']]
High scores: [{'name': 'Aarav', 'age': '16', 'score': '85'}, {'name': 'Rohan', 'age': '17', 'score': '92'}]
All methods must return generators (use yield). No data should be stored beyond what is needed for the current row.
class LazyCSV:
def __init__(self, data):
self.lines = data.strip().split('\n')
self.header = self.lines[0].split(',')
def rows(self):
for line in self.lines[1:]:
yield line.split(',')
def dicts(self):
for row in self.rows():
yield {self.header[i]: row[i].strip() for i in range(len(self.header))}
def select(self, columns):
indices = [self.header.index(col) for col in columns]
for row in self.rows():
yield [row[i].strip() for i in indices]
def where(self, column, condition):
for record in self.dicts():
if condition(record.get(column, '')):
yield record
csv_data = """name,age,score
Aarav,16,85
Priya,15,42
Rohan,17,92"""
csv = LazyCSV(csv_data)
row_gen = csv.rows()
print(f"Row: {next(row_gen)}")
dict_gen = csv.dicts()
print(f"Dict: {next(dict_gen)}")
names = list(csv.select(['name']))
print(f"Names: {names}")
high = list(csv.where('score', lambda s: int(s) > 50))
print(f"High scores: {high}")Challenge 9: Generator-Based Prime Sieve
HardImplement a prime number generator using the Sieve of Eratosthenes approach with generators: (1) integers_from(n) yields integers starting from n, (2) sieve(numbers) takes a generator of integers and yields primes by filtering out multiples, (3) primes() chains these to yield primes indefinitely. Use the generator to find the 100th prime and sum of primes below 100.
Sample Input
(No input required)
Sample Output
First 15 primes: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
100th prime: 541
Sum of primes below 100: 1060
Use generators throughout. The sieve function should recursively filter multiples.
def integers_from(n):
while True:
yield n
n += 1
def sieve(numbers):
prime = next(numbers)
yield prime
filtered = (n for n in numbers if n % prime != 0)
yield from sieve(filtered)
def primes():
yield from sieve(integers_from(2))
import sys
sys.setrecursionlimit(2000)
# First 15 primes
gen = primes()
first_15 = [next(gen) for _ in range(15)]
print(f"First 15 primes: {first_15}")
# 100th prime
gen = primes()
for i in range(99):
next(gen)
hundredth = next(gen)
print(f"100th prime: {hundredth}")
# Sum of primes below 100
gen = primes()
total = 0
for p in gen:
if p >= 100:
break
total += p
print(f"Sum of primes below 100: {total}")Need to Review the Concepts?
Go back to the detailed notes for this chapter.
Read Chapter NotesWant to learn Python with a live mentor?
Explore our Python course