Python Module 10
Iterators, generators and functional tools interview questions
Interview questions on iterators, generators and functional tools: warm-ups, then what each harder question tests and how a good answer starts.
Warm-up questions
Short questions with short answers, each answered in full.
-
Fresher to mid
What is the difference between an iterable and an iterator?
Answered in the lesson How iteration works: iterables and iterators
-
Fresher to mid
What does calling a generator function return, and when does its body run?
-
Fresher to mid
Why do you usually sort the data before calling itertools.groupby()?
-
Fresher
What do all([]) and any([]) return, and why?
Answered in the lesson Built-in functions every Python coder should know
Questions with model answers
What each question tests, the first step of a good answer and the follow-ups to expect.
-
Fresher to mid
A helper computes the total and then the average of the scores it is given. It works when callers pass a list, but a caller who passes map(float, row) gets ZeroDivisionError. Explain why, and change the helper so that it works for any iterable, including one too large for memory.
- What it tests
- The difference between iterables and one-shot iterators, designing functions that read their input once, and choosing between materialising the data and a single pass.
- First step
- The helper reads its argument twice, once in sum() and again to count it. A list can be read twice; a map object is an iterator and is empty the second time, so the count is 0. Then ask whether the helper needs the data twice at all: a total and a count fit in one loop.
- Follow-ups
-
- Why not call list() on the argument and be done?
- How would type hints say what the helper accepts?
- The caller now needs the median as well. What changes?
From the lesson How iteration works: iterables and iterators
Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.Opening the model answers with your pass…
Model answers open with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so the model answers can’t be opened at the moment. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
Design iteration for a Playlist class. Callers loop over it, sometimes in nested loops to compare songs, and another part of the program may add a song while a loop is running. What do __iter__() and the iterator do, and what should happen when the playlist changes during a loop?
- What it tests
- Separating an iterable from its iterators, restartable iteration, and choosing a policy for changes during a loop, the way dict and list behave.
- First step
- Make Playlist an iterable, not an iterator: __iter__() returns a new iterator object, or is a generator function, so every loop, nested ones included, gets its own position. Then choose what a change during a loop means: the loop sees the new song, fails loudly as dict does, or works on a snapshot.
- Follow-ups
-
- How does dict detect that it changed during a loop?
- Why is writing __iter__() as a generator function often enough?
- What does reversed() need from the class?
From the lesson How iteration works: iterables and iterators
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
read_batches(path, size) is a generator function that starts by raising ValueError when size is below 1. A job calls it with size 0 at start-up, but the error appears twenty minutes later in a worker thread, and the traceback never shows the bad call. Explain what happened, and fix the function and its tests.
- What it tests
- Knowing that a generator's body, checks included, runs only at the first next(), and splitting eager validation from lazy work.
- First step
- Explain the timing first: calling a generator function only creates the generator, so its check runs at the first next(), wherever and whenever that happens, and the traceback shows that place instead of the call. Then sort the function's work into what must happen at the call and what can wait for the first batch.
- Follow-ups
-
- How do you keep the name, docstring and signature on the outer function?
- Should the outer function open the file too, and who closes it?
- How would a test prove that the error is raised at the call?
From the lesson Generators and generator expressions
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A nightly report reads 50 million amounts from a database and prints their total, their average and the 10 largest. To save memory, a colleague replaced every list with a generator. Now the average and the top 10 are wrong, with no error. Explain why, estimate the memory involved, and design a version that is correct and memory-light.
- What it tests
- One-shot generators, estimating object memory, single-pass algorithms such as running totals and heapq.nlargest, and when building a list is the right trade.
- First step
- Count how often the report reads the data: three times, once each for the total, the average and the top 10. A generator can be read only once, so every pass after the first saw nothing. Then put numbers on the choices before you pick one: what 50 million amounts cost when held in a list, and what a single pass has to keep.
- Follow-ups
-
- Write the single pass. What state does it keep?
- How does heapq.nlargest keep its memory small?
- When is it right to build the list after all?
From the lesson Generators and generator expressions
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A generator that reads records with line = next(it) inside a while True loop worked for years. After a Python upgrade, every run now ends with RuntimeError: generator raised StopIteration. What changed, why was the change made, and how do you fix the code?
- What it tests
- PEP 479 and why a stray StopIteration was dangerous, and the idioms that end a generator correctly.
- First step
- Name the change first: since PEP 479, a StopIteration that escapes a generator's body becomes RuntimeError. Then find the line of the body that can raise StopIteration, and say what the code meant to happen when the input runs out.
- Follow-ups
-
- Why was a silent end dangerous?
- How does a generator end early on purpose now?
- Where else can a stray StopIteration cause silent bugs?
From the lesson Generators and generator expressions
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
Design a generator pipeline that reads a 20 GB web server log, keeps the requests slower than one second, and reports the ten slowest paths of each hour. Describe each stage, where memory is spent, and how you would test the stages.
- What it tests
- Splitting work into lazy stages, keeping state only where it is needed, bounding memory with heaps or counters, and testing stages separately.
- First step
- List the stages: read lines from the open file, parse each into a record, keep the slow ones, aggregate. Only the last stage keeps state, so memory depends on how many hours and paths it tracks, never on the 20 GB, as long as every earlier stage is a generator function that takes an iterable.
- Follow-ups
-
- A few lines are malformed. Where do you handle them?
- How do you keep only ten paths per hour without storing them all?
- The report must also show the total number of requests. Do you need a second pass?
From the lesson Generator pipelines, yield from and send
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A log pipeline that ran in 50 MB of memory starts using 9 GB after a small change, and the job is killed on busy days. The change adds sorted() to a middle stage so that records come out in time order. Explain what happened, and how you would get the order back without the memory.
- What it tests
- Recognising eager steps that break a lazy pipeline, measuring memory per stage, and order-preserving alternatives such as merging sorted runs.
- First step
- Find the eager step: sorted() must read every record before it can return the first one, so that stage holds the whole log in memory and every later stage waits for it. Before choosing a fix, check how far from time order the input really is, since logs are usually almost sorted already.
- Follow-ups
-
- How would you find the guilty stage with measurements?
- The input is sorted per server but not overall. What do you use?
- When is writing sorted chunks to disk the right answer?
From the lesson Generator pipelines, yield from and send
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Senior to staff
A web service exposes rows(query) as a generator that opens a database cursor and yields rows. Handlers often stop after the first page of results. After a busy hour, the database refuses new connections. Explain the leak and design a fix that keeps the convenience of the generator.
- What it tests
- Generator cleanup through GeneratorExit and close(), when finalisation really happens, context managers and contextlib.closing, and API design for resources.
- First step
- Show where the cursor is closed: in the generator's finally block, which runs only when the generator finishes or is closed. Then follow one handler that stops after the first page, and ask which of those two ever happens to its generator, and when.
- Follow-ups
-
- Why does it work fine in a quick test?
- How does contextlib.closing() help, and where does it go?
- Would you redesign the API as a context manager instead?
From the lesson Generator pipelines, yield from and send
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
A service splits one stream of events with itertools.tee() into two branches: one counts events per minute, the other writes them to an archive in batches whenever a timer fires. Memory climbs for hours until the process is killed. Explain why, and restructure the code.
- What it tests
- How tee() buffers items between branches, spotting branches that drift apart, and replacing tee() with one pass or a bounded queue.
- First step
- tee() keeps every item that one branch has read and another has not. The counting branch reads events as they arrive, while the archive branch reads only when its timer fires, so the gap, and the buffer, grows between timer runs, and without limit if the archive falls behind.
- Follow-ups
-
- When is tee() the right tool?
- How would you measure the buffer in production?
- The archive is slow on some days. How do you stop memory from growing without limit?
From the lesson itertools: the iteration toolkit
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to senior
A checkout tries every combination of the 30 coupons in a customer's wallet, using itertools.combinations for every size, to find the cheapest valid set. It passed its tests with 8 coupons and times out in production. Estimate the work, and redesign it.
- What it tests
- Estimating combinatorial growth before looping, reading the business rules for constraints, and choosing pruning, a greedy rule or dynamic programming over brute force.
- First step
- Count the work first: every combination of every size is every subset, 2 to the power 30, about 1.07 billion, against 256 for 8 coupons. Then ask about the rules, such as how many coupons may be combined, because they usually shrink the search far more than any speed-up of the loop.
- Follow-ups
-
- Only 3 coupons may be used together. How many sets is that?
- How would pruning cut the search?
- When is a greedy choice good enough?
From the lesson itertools: the iteration toolkit
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
You must send 1.2 million notifications through an API that accepts at most 500 recipients per request and 20 requests a second. Write the batching loop with itertools, estimate the run time, and say what happens to the last batch and to a request that fails halfway.
- What it tests
- Using batched() lazily, rate limiting, resuming after failures without sending twice, and estimating run time.
- First step
- Batch lazily: for batch in batched(recipients, 500) makes 2,400 requests, the last one full here because 1.2 million divides evenly by 500. At 20 requests a second that is 120 seconds at the very least, before any retry. Then design for failure: record which batches succeeded, so that a rerun can skip them.
- Follow-ups
-
- What does strict=True change, and do you want it here?
- How do you resume after a crash without sending twice?
- The recipients come from a database query. How do you keep memory flat?
From the lesson itertools: the iteration toolkit
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes
-
Mid to senior
A daily report prints max() and min() of temperature readings from 2,000 sensors. On some days both show nan, on others normal numbers, although the same sensors are broken every day. Explain the behaviour and fix the report.
- What it tests
- How NaN compares, how max() and min() choose their result, why the result depends on order, and handling missing data explicitly.
- First step
- Every comparison with NaN except != is false. max() keeps its first item and replaces it only when a later item compares greater, so when a NaN reading comes first nothing ever replaces it and the result is nan; when it comes later it is skipped. The result therefore depends on the order in which readings arrive, which changes from day to day.
- Follow-ups
-
- Why does sorted() not help, and what does it do with NaN?
- How should the report show the broken sensors?
- How would you test this behaviour?
From the lesson Built-in functions every Python coder should know
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to senior
An invoice adds 40,000 line amounts stored as floats with sum(). Finance reports totals that differ from the ledger by a few paise. A colleague proposes math.fsum(). Do you agree? Explain what sum() does with floats on Python 3.14, and how you would store and add money.
- What it tests
- Binary floating point, what sum() and math.fsum() guarantee, and choosing integer minor units or Decimal for money.
- First step
- Separate the two problems. Since Python 3.12, sum() of floats uses compensated summation and math.fsum() keeps exact partial sums, so the adding is rarely the cause now; the amounts themselves are binary approximations, and rounding lines differently from the ledger changes totals. Then ask how the ledger stores each amount and where it rounds.
- Follow-ups
-
- Why does 0.1 + 0.2 not equal 0.3?
- Integers of paise or Decimal, which do you choose and why?
- Where should rounding happen in an invoice?
From the lesson Built-in functions every Python coder should know
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A survey tool totals the numeric answers of a form with sum(v for v in answers if isinstance(v, int)). Every ticked checkbox, stored as True, adds 1 to the total. Explain why, write a check that accepts integers and floats but not booleans, and say when type(v) is int is the better test.
- What it tests
- bool as a subclass of int, isinstance() against exact type checks, union types in isinstance(), and validating input at the boundary.
- First step
- bool is a subclass of int, so isinstance(True, int) is True and True + True is 2. The filter therefore lets every True through as 1. Then decide exactly which values the check must refuse, and whether other subclasses of int, such as an IntEnum member, should still count.
- Follow-ups
-
- Why is bool a subclass of int at all?
- How do type hints and a type checker help here?
- Where in the program should this validation happen?
From the lesson Built-in functions every Python coder should know
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 2 minutes