Homework

11.3.3. Homework#

  • Use these questions after class to check your understanding of the chapter concepts and coding skills.

  • Sign in from the account menu before you start working on the homework.

  • Complete all true/false and coding questions before you submit the homework.

  • Launch Live Code (the rocket) to write and run code in the coding cells.

  • Multiple submissions are allowed. The highest score is kept.

1. Iterator Protocol

An object used with repeated next() calls is acting through the iterator protocol.

Hide code cell source

### Solution
# True
# The iterator protocol requires an __iter__() method and a __next__() method. Each call
# to next(obj) asks the iterator's __next__() for the next value.

2. Laziness

A generator can produce values one at a time instead of building a full list immediately.

Hide code cell source

### Solution
# True
# A generator produces each value only when it is requested and pauses between values. It
# never builds the full list in memory, which helps with large or unbounded data.

3. StopIteration

A custom iterator should return the string "done" forever when it runs out of values.

Hide code cell source

### Solution
# False
# When a custom iterator has no more values, __next__() should raise StopIteration. A for
# loop relies on that exception to end; returning "done" forever would make the loop run
# without stopping.

4. Generator Expressions

A generator expression is useful when a pipeline needs values lazily and does not need to store every intermediate result.

Hide code cell source

### Solution
# True
# A generator expression such as (x * 2 for x in rows) yields one value at a time. A
# pipeline can pass values from step to step without storing every intermediate result in
# a list.

5. Yield

A function that contains yield behaves exactly like a normal function that immediately returns a list.

Hide code cell source

### Solution
# False
# Calling a function that contains yield does not run its body; it returns a generator
# object. The body runs only as values are requested and pauses at each yield, instead of
# building and returning a list.
### 6. Use `iter` and `next`
# Create an iterator and read two values. Print exactly these two lines:
# # First: 10
# # Second: 20
values = [10, 20, 30]

### Your code starts here.



### Your code ends here.

Hide code cell source

### Solution
values = [10, 20, 30]
iterator = iter(values)
print("First:", next(iterator))
print("Second:", next(iterator))
First: 10
Second: 20
### 7. Write a generator function
# Define `countdown(n)`. Print exactly this line:
# # Countdown: [3, 2, 1]
### Your code starts here.



### Your code ends here.

Hide code cell source

### Solution
def countdown(n):
    while n > 0:
        yield n
        n -= 1

print("Countdown:", list(countdown(3)))
Countdown: [3, 2, 1]
### 8. Use a generator expression
# Sum only positive values using a generator expression. Print exactly this line:
# # Positive total: 14
values = [10, -3, 4, -1]

### Your code starts here.



### Your code ends here.

Hide code cell source

### Solution
values = [10, -3, 4, -1]
positive_total = sum(value for value in values if value > 0)
print("Positive total:", positive_total)
Positive total: 14
### 9. Handle iterator exhaustion
# Catch `StopIteration`. Print exactly these three lines:
# # Ana
# # Bob
# # No more names
names = iter(["Ana", "Bob"])

### Your code starts here.



### Your code ends here.

Hide code cell source

### Solution
names = iter(["Ana", "Bob"])

try:
    print(next(names))
    print(next(names))
    print(next(names))
except StopIteration:
    print("No more names")
Ana
Bob
No more names
### 10. Build a generator pipeline
# Build a three-stage generator pipeline over the ORDERS text:
#   1. Generator function parse_orders(text): skip the header row and
#      yield one (item, qty) tuple per line, with qty as an int.
#   2. Generator function large_orders(orders, minimum): yield only the
#      tuples whose qty is at least minimum.
#   3. Generator function shout(orders): yield (item.upper(), qty).
# Write pipeline(text, minimum) that connects the three stages and returns
# the last one. With minimum 3, build a fresh pipeline for each result:
# a list of the item names, the total quantity with sum(), and the largest
# quantity with max(). Print exactly these lines:
# # Items: ['PEN', 'MUG', 'PEN', 'BOOK']
# # Total quantity: 18
# # Largest order: 6
ORDERS = """item,qty
pen,5
book,2
mug,4
pen,3
lamp,1
book,6"""

### Your code starts here.



### Your code ends here.

Hide code cell source

### Solution
ORDERS = """item,qty
pen,5
book,2
mug,4
pen,3
lamp,1
book,6"""

def parse_orders(text):
    lines = iter(text.splitlines())
    next(lines)                          # skip the header row
    for line in lines:
        item, qty = line.split(",")
        yield item, int(qty)

def large_orders(orders, minimum):
    for item, qty in orders:
        if qty >= minimum:
            yield item, qty

def shout(orders):
    for item, qty in orders:
        yield item.upper(), qty

def pipeline(text, minimum):
    return shout(large_orders(parse_orders(text), minimum))

print("Items:", [item for item, qty in pipeline(ORDERS, 3)])
print("Total quantity:", sum(qty for item, qty in pipeline(ORDERS, 3)))
print("Largest order:", max(qty for item, qty in pipeline(ORDERS, 3)))
Items: ['PEN', 'MUG', 'PEN', 'BOOK']
Total quantity: 18
Largest order: 6