December Code

Python loops and control flow interview questions, with answers

Control flow is where short Python snippets become interview questions: a loop, an if, a break, and a question about what gets printed. The syntax is simple; what interviewers check is whether you know the details that decide the output — where range stops, when a loop's else runs, what a loop variable holds afterwards, and which tools replace the index-counting loops people bring from other languages. Every answer below comes with code you can run, and every output shown was produced by running it.

The questions go from range and the loop keywords to the idioms and newer syntax a modern answer is expected to use. Then take the free Python diagnostic — ten questions across every Python topic in the bank — to see which of these you can explain but not yet predict.

The questions, with answers

  1. 1.How does range() work, and why does it stop before the end value?

    In short: range(start, stop, step) produces start, start + step, and so on up to but not including stop — a half-open interval, so range(n) has exactly n values and ranges join without overlap.

    The half-open convention keeps the common cases clean: range(len(xs)) yields every valid index, range(0, 10) and range(10, 20) split a span with no overlap and no gap, and for a positive step the length is (stop - start) / step rounded up. A negative step counts down and still stops before stop, so range(10, 0, -3) never reaches 0. range is a lazy sequence, not a list: it supports len, indexing and in without building the values, so it costs the same memory for ten values or ten million. Wrap it in list() only when you need to see them.

    print(list(range(5)))
    # [0, 1, 2, 3, 4]
    print(list(range(10, 0, -3)))
    # [10, 7, 4, 1]
    print(len(range(2, 20, 5)))
    # 4
  2. 2.When does the else clause of a for or while loop run?

    In short: A loop's else runs when the loop finishes without hitting break — including when it never iterates at all — which makes it the natural place for a 'not found' result.

    Read else on a loop as 'no break'. A search loop breaks as soon as it finds what it wants; if it runs to the end, nothing was found, and the else block handles that case without a flag variable. The example below looks for a divisor of 7: none is found, no break happens, and the else prints. A return or an exception that leaves the loop skips the else too. The same clause works on while loops. Many experienced developers find the keyword misleading, which is exactly why interviewers ask about it; a comment such as # no break beside the else helps whoever reads the code next.

    for n in range(2, 7):
        if 7 % n == 0:
            break
    else:
        print("7 is prime")
    # 7 is prime
  3. 3.What is the difference between break, continue and pass?

    In short: break leaves the innermost loop at once, continue skips to its next iteration, and pass does nothing at all — it only fills a place where the syntax requires a statement.

    break and continue change the flow of the loop they are in, and only that loop: in nested loops, a break in the inner loop leaves the inner loop only. continue jumps straight to the next iteration, skipping the rest of the body, which is useful for filtering out cases early. pass is not a flow statement at all; it is a placeholder for an empty block, such as a stub function or an except clause you deliberately ignore, and it changes nothing when it runs. In the example, 1 is skipped by continue, and the loop ends at 3 before printing it.

    for i in range(5):
        if i == 1:
            continue
        if i == 3:
            break
        print(i)
    # 0
    # 2
  4. 4.How do enumerate and zip replace index-based loops?

    In short: enumerate yields (index, item) pairs and zip walks several iterables in step, so you rarely need range(len(...)); zip stops at the shortest input unless you pass strict=True.

    Looping with for i in range(len(xs)) and indexing xs[i] works, but it is the tell of code translated from another language. enumerate(xs, 1) gives a counter, starting wherever you like, alongside each item, and zip(a, b) pairs items from several sequences. The detail interviewers check is zip's length rule: it silently stops at the shortest input, so the third score below is dropped. Since Python 3.10, zip(a, b, strict=True) raises ValueError instead when the lengths differ, the safer choice whenever a mismatch would be a bug.

    names = ["meera", "kabir"]
    scores = [91, 78, 66]
    for i, (n, s) in enumerate(
            zip(names, scores), 1):
        print(i, n, s)
    # 1 meera 91
    # 2 kabir 78
  5. 5.Does a loop variable still exist after the loop ends?

    In short: Yes: a for loop's variable stays bound to its last value after the loop, but a comprehension's variable belongs to the comprehension and does not leak out.

    Python has no block scope. A for loop assigns to its variable in the enclosing function or module, so after the loop the name still holds the last item; if the loop never ran, it never assigned the name, and reading a name that was never assigned raises NameError. Comprehensions are different in Python 3: their variable lives in the comprehension's own scope, so it does not leak, as the second print shows. Code that relies on the leftover loop variable is legal but easy to misread; when you need the last item, name it explicitly.

    for i in range(3):
        pass
    print(i)  # 2
    
    sq = [j * j for j in range(3)]
    print("j" in globals())
    # False
  6. 6.How do you break out of nested loops in Python?

    In short: Python has no labelled break, so put the loops in a function and return, set a flag that the outer loop checks, or flatten the loops into one with itertools.product.

    break only leaves the innermost loop, and unlike Java or JavaScript, Python has no labels for breaking an outer one. The cleanest fix is usually a function: return exits every loop at once and hands back the answer. A boolean flag set before the inner break and tested after it also works, but it spreads the logic out. When the two loops are really one search over pairs, itertools.product turns them into a single loop, so an ordinary break is enough, as below. Raising and catching an exception works too, but reads as error handling and is best kept for genuine errors.

    from itertools import product
    
    pairs = product(range(1, 5),
                    range(1, 5))
    for a, b in pairs:
        if a * b == 6 and a < b:
            break
    print(a, b)  # 2 3
  7. 7.What does the walrus operator := do in a loop condition?

    In short: It assigns and returns a value inside an expression (Python 3.8 and later), so a while loop can fetch the next value and test it in one line instead of repeating the fetch.

    Without :=, a loop that reads until a sentinel has to fetch once before the loop and again at the end of the body, or run while True with a break. The assignment expression does both in the condition: while (n := next(it)) != 0 fetches the next item, binds it to n and compares it. The parentheses are required there, because := binds more loosely than !=. It also saves a repeated call inside comprehensions and if statements. An unparenthesised y := 5 is a SyntaxError as a statement of its own, and overusing the operator makes code dense; the fetch-and-test loop is where it clearly helps.

    data = [3, 1, 0, 4]
    it = iter(data)
    while (n := next(it)) != 0:
        print(n)
    # 3
    # 1
  8. 8.What does match-case add to Python?

    In short: Structural pattern matching (Python 3.10 and later): match tests a value against patterns that can check its shape and capture parts, runs only the first match, and never falls through.

    match is often called Python's switch, but it does more: a case can match literals, check the shape of a sequence or mapping, capture parts into names, try alternatives with |, and add an if guard. Only the first matching case runs, there is no fall-through and no break, and case _ is the catch-all. A bare name in a pattern captures rather than compares, so case STATUS: matches anything, and Python refuses to compile it ahead of other cases; compare against constants through a dotted name such as Status.OK, or a literal. For a simple value-to-value lookup, a dict is often still clearer.

    def kind(cmd):
        match cmd.split():
            case ["go", d]:
                return f"move {d}"
            case ["quit" | "exit"]:
                return "bye"
            case _:
                return "?"
    
    print(kind("go north"))
    # move north
    print(kind("exit"))  # bye

How the diagnostic asks it

One question from the Python bank, exactly as a sitting would show it. The bank has 3 on control flow and 34 across Python.

Control Flow · easyPY-007

What does this code print?

total = 0
for i in range(1, 10, 3):
    total += i
print(total)
  1. 122
  2. 218
  3. 37
  4. 412correct

range(1, 10, 3) produces 1, 4 and 7 and stops before 10, so the total is 12. 22 includes 10 as well, 18 is the sum of 0, 3, 6 and 9 from a range that starts at 0, and 7 is only the last value of the loop variable rather than the running total.

Measure it

Reading answers tells you what’s true. A diagnostic tells you what you get wrong.

10 Python questions across its topics, easy to hard, about fifteen minutes. You get a readiness figure with the arithmetic shown, the topics you missed named, and a practice set sized for today. Free: 1 diagnostic a month and 15 problems a day. No card.

What the readiness test measures · how the score is computed

By Harshit · updated