Python Module 3
Control flow: decisions, loops and pattern matching interview questions
Interview questions on “Control flow: decisions, loops and pattern matching”: 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
What is the difference between an if/elif chain and the same conditions written as separate if statements?
-
Fresher
When would you use a while loop rather than a for loop in Python?
Answered in the lesson while loops, break, continue and loop else
-
Fresher
How many times does the innermost line of two nested loops run?
-
Fresher
How does Python's match statement differ from a switch statement in C or Java?
-
Fresher
What does each of the three structural patterns check before it captures anything?
Answered in the lesson Structural matching: lists, dicts and classes
-
Fresher
What is the difference between the n and s commands in pdb?
Answered in the lesson Debugging with print, breakpoint() and pdb
-
Fresher
Why does trial division only need to try divisors up to the square root of n?
Answered in the lesson Solving problems with loops step by step
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 shop charges no delivery fee from 500, 40 from 200 to 499 and 70 below 200. Its code tests if total > 200 first, then elif total >= 500, then else. Orders of 650 are charged 40 and an order of exactly 200 is charged 70. Find both bugs, fix the chain and list the totals you would test.
- What it tests
- Ordering overlapping tests, choosing between > and >= at an edge, and picking test values on and just past every boundary.
- First step
- Run the chain by hand for 650: total > 200 is true first, so the 40 branch answers and the no-fee test below it can never run for any total. Then try 200 itself: 200 > 200 is false, so it falls through to the 70 branch.
- Follow-ups
-
- How would you write the chain so that the order of the tests no longer matters?
- Which totals make the smallest set of tests that catches both bugs, and why those?
- The thresholds change a few times a year. How do you keep them out of the code?
From the lesson Decisions with if, elif and else
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
-
Fresher to mid
Every order on a site is charged the express-shipping fee, standard orders included. The code reads if method == "express" or "overnight":. Explain the bug as you would to a new teammate, fix it, and say how a test and a linter would have caught it.
- What it tests
- The precedence of == and or, what or returns, the truth value of a string, and turning a bug into a test and a lint rule.
- First step
- Evaluate the condition for a standard order. == binds first, so it is (method == "express") or "overnight". The comparison is False, so or returns its right side, the string "overnight", and a non-empty string counts as true, for every order.
- Follow-ups
-
- Why does Python not warn about this condition?
- Is method in {"express", "overnight"} better than a tuple?
- How do you find other conditions with the same shape in the code base?
From the lesson Decisions with if, elif and else
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A script waits for a payment by asking the provider for its status every 5 seconds, in a loop written while status != "PAID":. One night a payment was declined, and the script was still running nine hours later. Explain why, and rewrite the loop so that it always stops and reports how it ended.
- What it tests
- A stopping condition that covers every outcome, bounding a loop by a deadline, and reporting how the loop ended.
- First step
- List every status the provider can send back. PAID is only one way for the wait to end: DECLINED and EXPIRED are final too, and the condition ignores them, so for those payments it can never become false.
- Follow-ups
-
- Why measure the deadline with time.monotonic() rather than time.time()?
- How can the else block of the loop help here?
- What should the loop do when the provider cannot be reached for a minute?
From the lesson while loops, break, continue and loop else
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A log cleaner walks a list of lines with i = 0 and while i < len(lines):, and skips blank lines with continue. On a file with one blank line it never finishes, and a processor core stays at 100 %. Diagnose the hang, fix it, and make the loop safe for the next person who edits it.
- What it tests
- Following a counter through continue, why the hang raises no error, and choosing a loop that cannot hang this way.
- First step
- Follow i through the pass that meets the blank line. continue goes back to the test before the i += 1 at the bottom of the body runs, so i never changes, and every pass after it reads the same blank line again.
- Follow-ups
-
- Why does the loop not raise an error?
- Is moving i += 1 above the continue enough?
- How would a test catch a hang like this before release?
From the lesson while loops, break, continue and loop else
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A nightly job finds the customers who are on both of two mailing lists, of 40,000 and 60,000 addresses, with one for loop inside another. As the lists grow it gets slower every month. Estimate the work it does, explain why it grows so fast, and rewrite it.
- What it tests
- Counting the work of nested loops, how it grows with the input, and replacing an inner scan with a set lookup, duplicates and letter case included.
- First step
- Count the comparisons: each of the 40,000 addresses is compared with each of the 60,000, so the inner line runs 40,000 × 60,000 = 2.4 billion times. Doubling both lists makes that four times larger.
- Follow-ups
-
- How many operations does a version with a set do for the same lists?
- Two addresses differ only in capital letters. Does your version treat them as one customer?
- When is a nested loop still the right choice?
From the lesson Nested loops and pattern printing
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A cinema booking function searches a 30-row hall for the first row with 4 free seats side by side, with two nested loops and a break, and books them. Testers find that it sometimes books seats in two rows at once. Explain why, fix it, and describe the tests you would add.
- What it tests
- What break leaves in nested loops, shaping a search that stops at its first find, and tests for the first, last and missing match.
- First step
- Look at what the break leaves: only the loop over seats. The loop over rows carries on, so every later row that also has 4 free seats side by side is searched, and booked, as well.
- Follow-ups
-
- Why is returning from a function usually the clearest fix?
- How would you write it with a flag instead, and what can go wrong with one?
- Which halls make the smallest set of tests that would have caught the bug?
From the lesson Nested loops and pattern printing
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A teammate writes case MAX_RETRIES: as the last case of a match on an attempt counter, meaning to compare it with the module constant MAX_RETRIES = 3. The tests pass. What does the case really do, why can the tests still pass, and how do you fix it so the mistake cannot come back?
- What it tests
- Capture patterns against value patterns, why a passing test suite can miss the trap, and the fixes: a literal, a dotted name or a guard.
- First step
- Read the pattern as Python does: a bare name is a capture pattern. case MAX_RETRIES: compares with nothing; it fits every attempt count that reaches it and binds that count to the name MAX_RETRIES.
- Follow-ups
-
- Why did Python not refuse the file, as it does when cases follow such a capture?
- Which fix would you choose: a literal, a dotted name or a guard?
- How would a test catch this mistake?
From the lesson Pattern matching with match and case
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A chat bot answers 25 typed commands with one match statement, and a new command is added every week. A teammate wants to replace the match with a dictionary that maps each command to a function. When is that the better design, when is match better, and what would you change first?
- What it tests
- The trade-offs between a match statement and a dispatch table: data or code, what patterns and guards add, testing, and adding commands safely.
- First step
- Look at what the 25 cases do. If each one only maps a fixed word to a reply or a function call, the match is a table written as code, and a dictionary holds the same table as data, one line per command.
- Follow-ups
-
- How do you handle an unknown command in the dictionary version?
- Which commands would push you back to match?
- How would you test that every command in the help text has a handler?
From the lesson Pattern matching with match and case
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A payment service sends webhook events as JSON: captured payments, failed payments and refunds, each with its own keys. Amounts sometimes arrive as text, and fields go missing. Replace a long chain of if and isinstance() checks with one match statement. How do you order the cases, and what happens to an event that no case fits?
- What it tests
- Writing one case per event shape with literals and type checks, the bool trap of int(), ordering specific shapes before general ones, and reporting events that fit no case.
- First step
- Write down each event type's shape: its "type" value, the keys it must have and the type of each value. Each shape becomes one mapping pattern, such as {"type": "refund", "id": str(rid), "amount": int(amount)}, and the cases go from the most specific to the most general.
- Follow-ups
-
- An amount arrives as true. Does int(amount) reject it?
- How do you report an event that no case fits without losing it?
- How would you test the match against malformed events?
From the lesson Structural matching: lists, dicts and classes
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Mid to senior
A service matches Order(order_id, total) positionally in a dozen places. A teammate reorders the data class's fields to total first, then order_id, for a nicer repr. No error appears, but some reports are now wrong. Why, and how do you make these patterns safe against the next change?
- What it tests
- How __match_args__ follows the field order, why positional patterns change meaning silently, keyword patterns and an explicit __match_args__, and a test that catches the swap.
- First step
- @dataclass builds __match_args__ from the fields in the order they are declared. After the change it is ('total', 'order_id'), so Order(order_id, total) binds the name order_id to the total and total to the id: the same pattern now means something else, and nothing checks it.
- Follow-ups
-
- Which kind of class pattern would have kept working?
- How do you keep the positions stable if the fields must move?
- Which test would have caught the swap?
From the lesson Structural matching: lists, dicts and classes
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A nightly job adds up the sales of 3,000 shops, read in pages of 50 from an API, and its total is about 2 % below the finance team's figure. Nothing raises an error, and the job runs on a server where nobody can use a debugger. How do you find the bug?
- What it tests
- Shrinking a silent bug to a small input with a known answer, comparing subtotals to find where the gap starts, labelled logging instead of a debugger, and a test that keeps the bug out.
- First step
- Make the gap small and checkable: compare the job's figures with the finance team's shop by shop, or page by page, for one day. About 2 % of 3,000 shops is about 60 shops, and 3,000 ÷ 50 is 60 pages, so check first whether every page loses one shop.
- Follow-ups
-
- The gap appears only on some days. What then?
- How do you log enough to find it without flooding the logs?
- Which test keeps this bug from coming back?
From the lesson Debugging with print, breakpoint() and pdb
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A breakpoint() left in a request handler made a teammate's local server freeze on the first request, while the same code in a container ended the worker with exit status 1. Explain both, and how you make sure no breakpoint() reaches production again.
- What it tests
- What pdb does with a terminal and with an empty input, why the two runs differ, and the layers that keep debugger calls out: PYTHONBREAKPOINT=0, a lint rule in CI and a search of each change.
- First step
- Ask where each process's standard input came from. On the laptop it was a terminal, so pdb printed its prompt and waited for someone to type. In the container nothing was attached, so pdb read the end of the input at once, took it as quit, and Python 3.14 then ended the process.
- Follow-ups
-
- Why is PYTHONBREAKPOINT=0 in production not enough on its own?
- How does a lint rule catch the call before it ships?
- When is a debugger on a running service still the right tool?
From the lesson Debugging with print, breakpoint() and pdb
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A coupon service calls a 12-digit code lucky when its number is prime. The check uses trial division with every divisor up to n − 1, and it times out on the first batch of 5,000 codes. Estimate why, fix it, and say what you would change if the batch grew to 5 million codes.
- What it tests
- Counting the work of a loop before timing it, the square-root bound, how much numbers that are not prime save, and choosing another algorithm when the count is still too large.
- First step
- Count before you time: for a 12-digit prime the loop up to n − 1 makes about 10¹² divisions. At the tens of millions of divisions a second that this track measures for Python, that is hours for a single prime code, so the batch cannot finish.
- Follow-ups
-
- Most codes are not prime. How does that change the estimate?
- Why can a sieve not list the primes up to 10¹² for this?
- How would you test the new check before it ships?
From the lesson Solving problems with loops step by step
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes
-
Fresher to mid
A billing module finds the gcd of two amounts with a loop that subtracts the smaller from the larger until they are equal. It is correct, yet gcd(10**12, 1) seems never to finish. Explain why, rewrite it, and use an invariant to argue that your loop is correct and always stops.
- What it tests
- Counting the passes of a loop, replacing repeated subtraction with %, an invariant and a shrinking value as the argument, and edge cases such as 0 and negative numbers.
- First step
- Count the passes: for (1012, 1) each pass takes 1 off the larger number, so the loop runs 10¹² − 1 times. a % b does every subtraction of the same b in one step: 1012 % 1 is 0 at once.
- Follow-ups
-
- Why does the remainder loop always stop?
- What should gcd(0, 0) and negative amounts return?
- When would you call math.gcd() instead?
From the lesson Solving problems with loops step by step
Model answer, Pro.In the answer:
- Model answer
- Follow-up answers
- Common mistakes
- Rubric
- Variants
- about 3 minutes