Python Skills Test for Hiring (2026): What to Test and Sample Questions

A level-by-level guide to assessing Python developers, with original questions and a rubric you can use today.

This Python skills test guide is for hiring managers and recruiters who need to check Python ability before a candidate reaches the engineering team. You get a skills matrix by level, 11 sample questions with what a good answer covers, a comparison of test formats and a 1 to 4 scoring rubric. The questions are different from our Python interview questions, so you can use one set for the screen and the other for a later round without repeats.

What a Python skills test should measure

AreaJuniorMidSenior
Language coreScope, closures, built-in typesIterators, generators and comprehensions used where they helpExplains interpreter behavior and edge cases to the team
Data handlingLists, dicts, sorting with a keyStreams large inputs, uses collections and itertoolsDesigns data models and validation for a service
Async and concurrencyKnows what async and await meanWrites asyncio code that never blocks the event loopStructured concurrency, cancellation and failure handling
Typing and toolingReads type hintsWrites typed functions, runs mypy or PyrightGenerics with 3.12 syntax, a typing policy for a codebase
TestingSimple pytest testsFixtures and mocking at the right boundaryTest strategy, triage of flaky tests
Debugging and performanceReads a tracebackProfiles before optimizingFinds memory growth and slow paths in production

Weight the rows to the job. A data engineering role leans on data handling, while an API role leans on async and testing. Trying to cover all six areas at full depth in one sitting makes the test long and the signal thin.

Sample Python test questions

Each question notes the level and format it suits. Pick 5 to 7 for one test, mixing at least one debugging question with the concept checks.

1. What does [f() for f in [lambda: i for i in range(3)]] return, and why?

Junior, concept

What good looks like: it returns [2, 2, 2]. Each lambda closes over the variable i, not its value when the lambda was made, and looks it up only when called. By then the inner comprehension has finished with i equal to 2. The usual fix is a default argument, lambda i=i: i, or functools.partial. Strong candidates call this late binding.

2. Why does count += 1 inside def bump(): raise UnboundLocalError when count = 0 is defined at module level?

Junior, debugging

What good looks like: an assignment anywhere in a function makes that name local for the whole function body. Python decides this when it compiles the function, so count += 1 tries to read a local that has no value yet. global count makes it work, but a better fix is to pass the value in and return the new one, or keep the state on an object. nonlocal is the equivalent for a name in an enclosing function.

3. Sort employee records by department A to Z, then by salary from highest to lowest.

Junior, practical

What good looks like: sorted(rows, key=lambda r: (r.dept, -r.salary)) works because salary is a number. When the secondary key can't be negated, such as a string, sort twice and rely on stability: first by the secondary key with reverse=True, then by department. Python's sort is stable, so equal departments keep the salary order from the first pass.

4. A loop deletes inactive users from a dict while iterating over it, and it crashes. What happened?

Junior, debugging

What good looks like: Python raises RuntimeError: dictionary changed size during iteration. Iterate over a snapshot with for key in list(users):, or build a new dict with a comprehension such as {k: v for k, v in users.items() if v.active}. The comprehension is usually clearer and avoids mutating shared state.

5. Why does round(2.5) return 2 while round(3.5) returns 4?

Junior to mid, reasoning

What good looks like: Python 3 rounds halves to the nearest even number (banker's rounding), which avoids a steady upward bias across many values. A strong candidate adds that floats can surprise you too: round(2.675, 2) gives 2.67 because 2.675 can't be stored exactly. For money, use decimal.Decimal with an explicit rounding mode such as ROUND_HALF_UP.

6. In a match statement, why does case NOT_FOUND: match every value?

Mid, concept

What good looks like: a bare name in a case is a capture pattern. It binds the subject to that name, so it always matches, and Python raises a SyntaxError if that catch-all comes before other cases. To compare against a constant, use a dotted name such as HTTPStatus.NOT_FOUND, or a literal like 404.

7. Split a stream of 10 million IDs into batches of 500 for a bulk API.

Mid, practical

What good looks like: Python 3.12 added itertools.batched(ids, 500), which yields tuples and holds only one batch at a time. On older versions, a loop over itertools.islice does the same job. Good answers keep the input lazy (no list of 10 million IDs first) and handle the short final batch.

8. An async web handler calls time.sleep(2) and requests.get(). Under load, every request slows down. Why?

Mid, debugging

What good looks like: both calls block the event loop thread, so no other coroutine runs while they wait. Use await asyncio.sleep(), an async HTTP client such as httpx or aiohttp, and push any blocking call you can't avoid to a thread with await asyncio.to_thread(...).

9. What does asyncio.TaskGroup give you that asyncio.gather doesn't?

Senior, concept

What good looks like: TaskGroup, added in 3.11, gives structured concurrency. Tasks start inside an async with block that doesn't exit until all of them finish. If one task fails, the group cancels the others and raises an ExceptionGroup, which you handle with except*. With default settings, gather passes on the first exception but leaves the other tasks running.

10. Python 3.12 allows def first[T](items: list[T]) -> T and type Pair = tuple[int, int]. What do they solve?

Senior, concept

What good looks like: this is the type parameter syntax from PEP 695. Generics are declared inline, without module-level TypeVar boilerplate, and the type statement creates an explicit type alias that is evaluated lazily. A strong candidate notes that type checkers enforce it while the runtime doesn't, and that code which must run on 3.11 or older can't use it.

11. A long-running service's memory climbs for days until it restarts. Where do you look?

Senior, practical scenario

What good looks like: measure first. Compare tracemalloc snapshots taken hours apart to find allocation sites that keep growing, or use a heap profiler such as memray. Common causes are a module-level list or dict that is never trimmed, and functools.lru_cache(maxsize=None) on a method, which also keeps every self alive. After the fix, run the same measurement to prove the growth stopped.

Test formats compared

FormatWhat it showsWeaknessBest for
Multiple choice quizRecall of syntax and built-in behaviorEasy to look up or guess; says little about real codeA short filter for junior roles
Take-home projectHow they structure a small package, tests and a READMEHours of unpaid time; hard to know who wrote itA final check for mid roles, kept under 3 hours
Live codingReasoning out loud and debugging with light pressureNerves hide skill; costs engineer time per candidateSecond rounds for mid and senior roles
Work sampleReal tasks, like fixing a failing test or reading a traceback from your stackTakes effort to build and keep currentRoles with a clear daily task, such as data pipelines
Adaptive AI interviewDepth, since questions get harder or easier with each answer and follow-ups ask whyNot a full project build; check that it covers your stackA consistent first round before engineers spend time

Most teams combine two: a short screen for depth, then a work sample or live round for finalists. If any step is unproctored, read how to detect AI interview cheating first.

Scoring rubric

ScoreWhat you see
1Wrong or memorized answers. Can't explain why the code behaves as it does, even with a hint.
2Knows the right term (stable sort, late binding) but can't apply it to a new case without heavy help.
3Correct, working answers with sensible trade-offs. Uses a hint well and reaches the fix.
4Spots edge cases unprompted, measures before optimizing, and explains clearly enough to teach a teammate.

Running a Python test with NTRVSTA

Python is part of NTRVSTA's exam library. The live AI interviewer asks a question, listens to the answer and adapts difficulty to it: a candidate who explains late binding cleanly moves on to async or typing, while one who struggles gets a simpler question. It can offer a hint and note how the candidate uses it, and it follows up on reasoning instead of accepting a bare answer. For senior backend roles, a system-design question runs on an interactive whiteboard.

Each interview produces a score breakdown (technical skills, English proficiency, communication), video snippets of key moments and a shareable report your engineers can review in minutes. For live-round questions, see our Python interview questions. To compare dedicated coding test platforms, see the best coding assessment tools. Hiring across stacks? We also cover JavaScript and Java skills tests, plus a data scientist job description.

Questions

How long should a Python skills test be?

Aim for 30 to 45 minutes for a first round and under 3 hours for any take-home. Longer tests lose strong candidates who have other offers, and the extra time rarely changes the decision.

Should candidates be allowed to use AI tools during a Python test?

Decide by what the job allows. If your engineers use AI assistants at work, a take-home can allow them, but then ask the candidate to explain and change the code live. For a reasoning round, ask them not to, and use a format where they talk through answers out loud.

Which Python version should the test assume?

Python 3.12 or newer is a reasonable baseline in 2026. Tell candidates the version up front, and don't mark anyone down for missing a feature newer than the version your team runs.

Curious how an adaptive Python round feels from the candidate's side? Book a 20-minute demo, or try a sample interview yourself.

NTRVSTA
‍