These Python interview questions are for hiring managers running a first or second technical round, and for candidates who want to know what a good answer sounds like. Each question comes with what a strong answer covers and, where it helps, a red flag to listen for. Pick 6 to 8 that match the role's level instead of running all of them, and ask "why" after every answer. The follow-up tells you more than the first reply.
What a strong Python interview checks
- Core language model: mutability, references, and how Python passes and copies objects.
- Idiomatic code: generators, comprehensions, context managers and decorators used where they help, not to show off.
- Performance sense: picking the right data structure and measuring before optimizing.
- Concurrency judgment: knowing when threads, processes or asyncio fit the problem.
- Testing and reliability: clear error handling, tests that don't hit real services, and reproducible environments.
- Collaboration: explaining trade-offs, owning mistakes, and handling code review well.
Python interview questions: fundamentals
1. Which built-in types are mutable, and why does that matter for dictionary keys?
What good looks like: lists, dicts, sets and bytearrays are mutable. Ints, floats, strings, tuples, frozensets and bytes are immutable. Dict keys and set members must be hashable, which in practice means immutable. A strong candidate adds that a tuple is only hashable if everything inside it is, so (1, [2, 3]) can't be a key.
Red flag: saying any tuple works as a key.
2. What's wrong with def add(item, items=[])?
What good looks like: default values are evaluated once, when the def statement runs, not on every call. Every call that leaves out items shares and grows the same list. The fix is items=None, then if items is None: items = [] inside the function.
Red flag: they can't explain why the list keeps growing between calls.
3. What's the difference between is and ==?
What good looks like: == compares values (it calls __eq__), while is checks whether two names point to the same object. Use is for singletons like None, as in if x is None.
Red flag: using is to compare numbers or strings because it "worked in the REPL." Small-integer caching and string interning are CPython implementation details.
4. Why is checking membership in a set faster than in a list?
What good looks like: x in my_list scans items one by one, which is O(n). Sets and dicts use a hash table, so lookups are O(1) on average. The cost is that members must be hashable and the set uses more memory. A strong answer mentions converting a list to a set once before a loop that checks membership many times.
5. Explain shallow copy vs deep copy.
What good looks like: a shallow copy (copy.copy(x), list(x) or x[:]) makes a new outer container but shares the objects inside it. A deep copy (copy.deepcopy(x)) recursively copies the inner objects too. With a list of lists, changing an inner list through a shallow copy also changes the original.
6. What do *args and **kwargs do?
What good looks like: *args collects extra positional arguments into a tuple, and **kwargs collects extra keyword arguments into a dict. Strong candidates also know that parameters after a bare * are keyword-only, and parameters before a / are positional-only (Python 3.8 and later).
7. What is a generator, and when would you use one?
What good looks like: a function that uses yield returns a generator, which produces values one at a time, only when asked. That keeps memory flat when you process large files or streams. A generator expression like sum(x * x for x in nums) avoids building a full list. A good answer notes that a generator can only be consumed once.
Red flag: calling list() on a huge generator "to be safe," which throws away the benefit.
8. How does a decorator work?
What good looks like: a decorator is a callable that takes a function and returns a replacement, usually a wrapper. @timer above a function is shorthand for func = timer(func). Strong candidates use functools.wraps so the wrapper keeps the original name and docstring, and know that a decorator with its own arguments needs one more layer of nesting.
9. What problem does a context manager solve?
What good looks like: with guarantees cleanup, such as closing a file or releasing a lock, even if an exception is raised. You can write one as a class with __enter__ and __exit__, or as a generator decorated with contextlib.contextmanager, using try and finally around the yield.
Errors, types and concurrency
10. What is the GIL, and how does it affect your choice between threads and processes?
What good looks like: in CPython, the Global Interpreter Lock lets only one thread run Python bytecode at a time. Threads still help with I/O-bound work, because a thread waiting on the network or disk releases the lock. For CPU-bound pure-Python work, use processes (multiprocessing or ProcessPoolExecutor) or libraries like NumPy that do heavy work outside the lock. A strong candidate may mention that CPython 3.13 added an optional free-threaded build, which is not the default.
Red flag: "Python can't do concurrency."
11. Walk me through try, except, else and finally.
What good looks like: except handles specific exceptions, else runs only if no exception was raised, and finally always runs. Catch the narrowest exception you can handle, and use raise NewError(...) from err to keep the original cause in the traceback.
Red flag: a bare except: that swallows everything, including KeyboardInterrupt and SystemExit.
12. Are type hints enforced at runtime?
What good looks like: no. The interpreter doesn't check them. Tools like mypy or Pyright check them before the code runs, and some libraries (Pydantic, for example) read them to validate data. Strong candidates know modern syntax such as list[int] (3.9 and later) and str | None (3.10 and later), and see hints as documentation that a tool can verify.
13. When would you use a dataclass?
What good looks like: for classes that mainly hold data. @dataclass generates __init__, __repr__ and __eq__ for you. frozen=True makes instances immutable, and hashable when equality is also generated. Mutable defaults need field(default_factory=list), because a plain list default raises an error.
Practical scenarios
14. Count the 10 most common words in a 5 GB log file.
What good looks like: read the file line by line (a file object is a lazy iterator), normalize case with lower() or casefold(), split into words, and feed them to collections.Counter. Then call most_common(10). Memory grows with the number of distinct words, not the file size.
Red flag: f.read() on the whole file.
15. Remove duplicates from a list but keep the original order.
What good looks like: list(dict.fromkeys(items)), which works because dicts keep insertion order (guaranteed since Python 3.7). A loop with a seen set also works and is easy to adapt. For unhashable items like dicts, they dedupe on a hashable key instead.
Red flag: list(set(items)), which loses the order.
16. A function that used to take 2 seconds now takes 40. How do you find out why?
What good looks like: measure before changing anything. Profile with cProfile, time small pieces with timeit, and check whether the input grew. Common causes include list membership checks inside a loop, building a string with += in a loop instead of "".join(parts), and one database query per row. A strong candidate fixes the algorithm first and micro-optimizes last.
17. You need to call an external API 500 times. How do you make it fast without getting blocked?
What good looks like: this is I/O-bound, so use a thread pool (ThreadPoolExecutor) or asyncio with an async HTTP client. Cap concurrency, for example with asyncio.Semaphore, set timeouts, retry with backoff on errors, and respect the API's rate limits.
Red flag: firing all 500 requests at once with no timeouts.
18. How would you test code that calls a payment API?
What good looks like: never hit the real service in unit tests. Use pytest with fixtures, and replace the client with unittest.mock.patch or a fake passed in through the constructor. A strong candidate knows to patch the name where it's looked up (in the module that uses it), not where it's defined, and tests error paths like timeouts and declined payments.
Behavioral (STAR)
19. Tell me about a bug you shipped in Python code. How did you find and fix it?
What good looks like: a clear Situation and Task, then specific Actions (how they reproduced it, what they logged, the test they added), and a Result with a lasting fix, such as a new test or alert. They own the mistake without blaming others.
Red flag: no example, or a story where someone else was always at fault.
20. Describe a time you made a codebase easier to work with.
What good looks like: a concrete change (splitting a large module, adding type hints, removing dead code), why it was worth the time, how they kept it safe with tests, and a measurable Result like fewer bugs or faster onboarding.
21. Tell me about a disagreement in a code review.
What good looks like: they explain the other person's view fairly, back their own view with evidence such as a benchmark or a doc, and describe how the team reached a decision. Good candidates can name a time they changed their mind.
A simple scoring rubric
Running this interview with AI
If you screen many Python candidates, an adaptive AI interviewer can run this first round for you. NTRVSTA's exam library includes Python, and the interviewer adjusts difficulty to each answer, so strong candidates move on to harder questions while those who struggle get easier ones. Behavioral questions follow the STAR method, and a hint system shows how a candidate takes guidance, which is useful for junior roles.
Each interview ends with a score breakdown (technical skills, English proficiency, communication) and video snippets of key moments, so your engineers review evidence instead of sitting through every first call. See how we compare tools in the best AI interview software, and read how AI interviews are scored before you trust any automated score. For related roles, see our software engineer, backend developer and data scientist interview questions, or brush up on the STAR method.
Questions
How many questions should a first-round Python interview have?
For a 45-minute round, plan 6 to 8: four or five fundamentals at the right level, one or two practical scenarios, and one behavioral question. Leave time for follow-ups, since they show real depth.
Should candidates be allowed to look things up during the interview?
For fundamentals, no, because you want to hear how they think. For practical scenarios, allowing documentation is closer to real work. Decide in advance and tell every candidate the same rule.
How do I adjust these questions for junior vs senior Python roles?
Ask juniors the fundamentals and score clear reasoning. For seniors, spend most of the time on scenarios like the slow function and the API calls, and push on trade-offs, testing and how they'd explain the choice to a team.
Want to see a Python interview run by an adaptive AI interviewer? Book a 20-minute demo, or try a sample interview yourself.




