Python Interview Mock sample questions with answers

10 questions from the Python Interview Mock practice bank, spread across its domains. Pick your answer, then open the explanation to see why each option is right or wrong.

  1. Question 1Language Core

    What does this print?

    a = [2, 4, 6, 7]
    for x in a:
        if x % 2 == 0:
            a.remove(x)
    print(a)
    • A

      [7]

    • B

      [4, 7]

    • C

      It raises RuntimeError: list changed size during iteration

    • D

      [2, 4, 6, 7]

    Show the answer and explanation

    Answer: B

    A for loop over a list walks an index. Step 1: index 0 is 2, which is removed, giving [4, 6, 7]. Step 2: index 1 is now 6, which is removed, giving [4, 7]. Step 3: index 2 is past the end, so the loop stops. Build a new list instead: a = [x for x in a if x % 2], or iterate over a copy (for x in a[:]).

    Why the other options are wrong

    • A. That assumes every even number is visited, but removing items shifts the rest under the loop.

    • C. Lists do not detect modification during iteration; only dicts and sets raise this error.

    • D. remove does change the list: 2 and 6 are removed.

  2. Question 2Ecosystem and Practice

    This decorator is typed loosely. What type does mypy reveal for sub, and is the bad call reported?

    from typing import Any, Callable
    
    def loose(fn: Callable[..., Any]) -> Callable[..., Any]:
        return fn
    
    @loose
    def sub(x: int, y: int) -> int:
        return x - y
    
    reveal_type(sub)
    sub("a", 1)
    • A

      def (*Any, **Any) -> Any, and the call is not reported

    • B

      def (x: int, y: int) -> int, and the call is reported

    • C

      Callable[..., int], and the call is reported

    • D

      Any, and mypy reports that sub is untyped

    Show the answer and explanation

    Answer: A

    The return annotation Callable[..., Any] is all mypy knows about the decorated name, so it reveals def (*Any, **Any) -> Any and accepts any arguments. That is how a careless decorator silently switches off checking for every function it touches. Typing it with ParamSpec and TypeVar preserves the signature.

    Why the other options are wrong

    • B. mypy uses the decorator's declared return type, not the original function's.

    • C. The return type is Any as well, and ... accepts any arguments.

    • D. It reveals a callable type, and no error is emitted.

  3. Question 3Objects and Protocols

    Given r = range(3), the expression list(r) == list(r) is True.

    • A

      True

    • B

      False

    Show the answer and explanation

    Answer: A

    range objects are immutable sequences that support indexing, len() and repeated iteration. Only the iterator you get from iter(r) is single-pass. Contrast with map, zip and generators, which are exhausted after one list().

    Why the other options are wrong

    • B. That would happen with an iterator like iter(range(3)), not a range object.

  4. Question 4Concurrency and Performance

    A billing service switches from float to decimal.Decimal. Additions get roughly 3× slower in a micro-benchmark on CPython 3.13. Which reasoning is correct?

    • A

      Decimal is software arithmetic; worth it for money, which needs exact decimal results

    • B

      Decimal is written in pure Python, so moving the service to PyPy will remove the gap

    • C

      The slowdown proves floats are exact enough, so Decimal is only needed above 2**53

    • D

      Decimal is slow only because each operation recreates the context; caching it removes the cost

    Show the answer and explanation

    Answer: A

    A float addition is one CPU instruction on IEEE 754 doubles. A Decimal addition goes through libmpdec's software arithmetic plus object allocation. Measured on 3.13 it was about 3× slower per add. For money the exactness wins; storing integer cents is the other fast, exact option.

    Why the other options are wrong

    • B. CPython uses the C implementation (_decimal/libmpdec); the cost is software arithmetic, not Python.

    • C. Floats can't represent 0.1 exactly at any magnitude; that's the reason to use Decimal for money.

    • D. The context is thread-local and reused; the cost is the arithmetic itself.

  5. Question 5Language Core

    What does this print?

    class Order:
        @property
        def total(self):
            return (1).discount   # a typo: ints have no .discount
    
        def __getattr__(self, name):
            return f'<missing {name}>'
    
    print(Order().total)
    • A

      It raises AttributeError: 'int' object has no attribute 'discount'

    • B

      <missing discount>

    • C

      <missing total>

    • D

      None, because the property returns nothing

    Show the answer and explanation

    Answer: C

    __getattr__ runs whenever normal attribute lookup raises AttributeError, and that includes an AttributeError raised inside a property's getter. The real bug (the typo inside total) is masked by the fallback. Keep __getattr__ narrow, or re-raise unknown names, to avoid hiding errors like this.

    Why the other options are wrong

    • A. That error is raised, but an AttributeError from a property triggers the __getattr__ fallback.

    • B. __getattr__ is called on the Order instance, so it receives the Order's attribute name.

    • D. The property doesn't return; it raises before reaching any return value.

  6. Question 6Ecosystem and Practice

    What happens when this test class runs?

    import unittest
    
    class TestDB(unittest.TestCase):
        def setUpClass(cls):
            cls.db = connect()
    
        def test_query(self):
            self.assertTrue(self.db.ok)
    • A

      It works: unittest calls setUpClass once with the class

    • B

      A TypeError for the missing cls; it needs @classmethod

    • C

      unittest calls it before every test, like setUp

    • D

      It is silently skipped, and the test fails with AttributeError on self.db

    Show the answer and explanation

    Answer: B

    setUpClass and tearDownClass run once per test class and must be declared with @classmethod. unittest invokes TestDB.setUpClass() with no arguments, so a plain function raises TypeError: TestDB.setUpClass() missing 1 required positional argument: 'cls'. That is reported as an error, and none of the class's tests run. Verified on Python 3.13.

    Why the other options are wrong

    • A. unittest calls it on the class, so without @classmethod the cls argument is never filled.

    • C. setUp runs per test; setUpClass runs once per class and must be a classmethod.

    • D. It isn't skipped: calling it raises a TypeError, reported as a class-level error.

  7. Question 7Objects and Protocols

    What does this print?

    def find(xs, target):
        for i, v in enumerate(xs):
            if v == target:
                print('found', i)
                break
        else:
            print('missing')
    
    find([1, 2, 3], 2)
    find([1, 2, 3], 9)
    find([], 1)
    • A

      found 1, missing, and nothing for the empty list

    • B

      found 1, missing, missing

    • C

      found 1, missing, missing, missing

    • D

      found 1 missing, missing, missing

    Show the answer and explanation

    Answer: B

    A loop's else clause runs when the loop terminates normally, meaning it ran out of items, including when there were none, and is skipped when the loop exits via break. That makes it a natural search-failed branch: the first call breaks, so it prints only found 1, while the other two calls fall through to missing. Verified on Python 3.13.

    Why the other options are wrong

    • A. The else runs whenever the loop ends without break, including after zero iterations.

    • C. else is skipped when break runs, so the first call prints only once.

    • D. After break, the else block is skipped entirely.

  8. Question 8Concurrency and Performance

    On CPython 3.12 or later, sum([0.1] * 10) == 1.0 evaluates to True, even though adding 0.1 ten times in a plain for loop gives 0.9999999999999999.

    • A

      True

    • B

      False

    Show the answer and explanation

    Answer: A

    Python 3.12 changed sum() to use a compensated (Neumaier-style) algorithm for floats, so sum([0.1] * 10) is exactly 1.0. A manual t += 0.1 loop still accumulates rounding error. math.fsum has always been exact to within one rounding and remains the tool when correctness matters on any version.

    Why the other options are wrong

    • B. This was true before 3.12; newer sum() tracks the lost low-order bits as it adds.

  9. Question 9Language Core

    What happens when this module is imported?

    NOT_FOUND = 404
    
    def check(code):
        match code:
            case NOT_FOUND:
                return 'not found'
            case _:
                return 'other'
    • A

      It imports fine, and check(200) returns 'other'

    • B

      SyntaxError: the name capture NOT_FOUND makes the remaining patterns unreachable

    • C

      It imports fine, but check(200) raises NameError for NOT_FOUND

    • D

      It imports fine, and check(404) returns 'not found' while check(200) returns 'other'

    Show the answer and explanation

    Answer: B

    In match, a bare name like NOT_FOUND is a capture pattern: it matches anything and binds the subject to that name. Because it can never fail, a later case _: is unreachable, and CPython raises SyntaxError: name capture 'NOT_FOUND' makes remaining patterns unreachable at compile time. To compare against a constant, use a dotted name (HTTPStatus.NOT_FOUND, codes.NOT_FOUND) or a guard (case x if x == NOT_FOUND). Verified on Python 3.13.

    Why the other options are wrong

    • A. A bare name in a case is a capture pattern, not a comparison with the constant.

    • C. The pattern binds NOT_FOUND as a new local rather than reading the global.

    • D. That needs a value pattern such as a dotted name; a bare name captures.

  10. Question 10Ecosystem and Practice

    What does load() return?

    def load():
        try:
            raise FileNotFoundError('cfg')
        except OSError:
            return 'OSError'
        except FileNotFoundError:
            return 'FNF'
    • A

      'OSError'

    • B

      'FNF'

    • C

      It raises SyntaxError for an unreachable clause

    • D

      It returns both, as a tuple

    Show the answer and explanation

    Answer: A

    Except clauses are tested top to bottom, and the first matching one wins. FileNotFoundError is a subclass of OSError, so the first clause matches and the more specific clause below it can never run. Put specific exceptions before their parents (linters flag this).

    Why the other options are wrong

    • B. Python does not look for the most specific clause; it takes the first match.

    • C. Python accepts the code; only linters warn about it.

    • D. Only one except clause ever runs for a given exception.

Practise all 485 PY questions

Start with the free 15-question diagnostic. It shows where to focus, and your results carry over if you sign up.

Go to PY