Python interview questions open nearly every DevOps, SRE, platform, and backend hiring loop, because Python is the glue language of modern infrastructure: cloud SDKs, automation, CI glue, and half the internal tooling you will ever maintain. This page is a complete, tiered bank: 104 questions with worked answers, split into beginner, intermediate, and scenario sections, plus hands-on labs, a cheat sheet of the calls behind the answers, and a knowledge check at the end. Work through it in order, or jump straight to the tier that matches your next round.

TL;DR
- 104 questions, three tiers. Daily-driver fluency, interpreter mechanics like the GIL, mutability, and decorators, and realistic debugging scenarios, each answer shaped the way real Python interview questions are graded.
- Answers explain the mechanism. Every answer names the component that owns the behavior, gives the call or flag, and adds the one caveat that separates a pass from a strong hire.
- Practice is built in. Three scenario labs, a cheat sheet of the commands and calls that come up most, and a five-question quiz to check what stuck.
How the Python interview works in 2026
Most Python interview questions arrive in one of three rounds. The first is a screening call, often with a hiring manager or a senior engineer, that mixes quick language checks with tooling checks: list versus tuple, is versus ==, what a virtualenv is for, why installing into system Python is frowned on. These are confidence filters. Nobody expects a lecture; they expect a crisp 30-second answer with one level of detail behind it, delivered without hesitation.
The second round is the deep technical. This is where Python interview questions get mechanical: what the GIL does and does not serialize, why a mutable default argument is evaluated once, how a generator differs from a list, what a decorator compiles into, how imports resolve. Interviewers at this stage listen for whether your knowledge has edges. Saying threads are for I/O passes a beginner bar; explaining that the GIL is released during blocking calls, and what that means for a mixed workload, passes a working-engineer bar. Interviewers grade Python interview questions on that second sentence, not the first.
The third round is the scenario round, sometimes live, sometimes a screen share over a broken repo, increasingly a take-home against a container. You get a broken script in one sentence: it worked on my laptop and dies in CI, the worker eats memory over a day, the pipeline hangs with no output. What is being scored is your diagnostic order, not your luck. Engineers who say first I would confirm the interpreter, then profile, then instrument outperform engineers who start patching. Most scenario Python interview questions are lost here, not on knowledge.
Role shape changes the emphasis. DevOps and platform roles weight Python interview questions toward scripting mechanics: subprocess, environments, packaging, parsing logs and APIs, and the judgment to keep scripts boring and testable. SRE roles add profiling and concurrency under load: cProfile, py-spy, thread pools versus asyncio. Backend roles go deeper on data modeling and frameworks but still open with the same core language mechanics. In every case, the base bank on this page is the same; only the depth expectation moves.
Preparation that works is boringly mechanical. Type every snippet in this bank at least once, break it on purpose, and rehearse each answer out loud until it takes 30 to 60 seconds spoken. If you can narrate the GIL, the aliasing model, and one deadlock story fluently, you are ahead of most candidates, because most candidates cram lists and never say the answers. Pair the page with a scratch container and a throwaway venv, and treat the scenario tier like a rehearsal, because that is exactly what the round is.
Core concepts interviewers expect you to know
Three mental models unlock most of the technical depth in Python interview questions: names and objects, the interpreter loop with its GIL, and the environment model that decides which interpreter and which packages actually run. Interviewers open here because every other topic is an extension of one of these three. Narrate all three fluently and follow-up questions stop feeling like new topics.
Names, objects, and mutability
Python names are references bound to objects. Assignment never copies data: b = a makes both names point at the same list, so b.append(4) is visible through a. What splits the language in half is mutability. Lists, dicts, and sets change in place; ints, strings, and tuples cannot, so every apparent change to them rebinds the name to a new object. This one distinction explains the aliasing surprises, the mutable default argument trap, why dict keys must be hashable, and half the bug reports in scripted pipelines.
The model pays off immediately in interviews. When an interviewer shows two variables and a mutation, the strong candidate narrates which objects exist, which names point where, and which operation mutates versus rebinds. Weak candidates memorize a rule per case and run out of rules. Since a huge share of beginner Python interview questions reduce to do you know which objects are shared here, rehearse this model until drawing the arrows is automatic.
The interpreter loop and the GIL
CPython compiles your source to bytecode and executes it on a stack machine inside one loop, guarded by the Global Interpreter Lock: only one thread executes Python bytecode at a time per process. The lock is released during blocking I/O and inside many C extensions, which is why threads still help network-bound scripts while doing nothing for CPU-bound loops. The consequences cascade through everything else: threads for waiting, processes for computing, asyncio for massive I/O fan-out, and profilers that must respect the lock. This model sits under the longest chains of intermediate Python interview questions, so learn to draw it from memory.
Environments, imports, and the machine view
The third model is environmental. A Python process sees packages through sys.path: the script’s directory, then PYTHONPATH, then the site-packages of whichever environment owns the running interpreter. A virtual environment is a thin directory that redirects that last stop, which is how projects keep conflicting dependency graphs apart. Most works on my machine stories, most ModuleNotFoundError tickets, and a solid share of scenario Python interview questions collapse to one fact: the interpreter that ran is not the interpreter you configured. Print sys.executable before anything else and half the mystery disappears.
Question bank at a glance
The bank below is organized the way Python interview questions are actually asked: broadly increasing difficulty, then the scenario round that finishes most loops. Counts are exact, so you can pace a week of prep: roughly 15 beginner questions a day gets you through the bank in a working week, with the labs on the weekend.
| Section | Questions | What it proves |
|---|---|---|
| Beginner | 38 | You write Python daily and know the data structures cold. |
| Intermediate | 40 | You know the mechanisms: the GIL, mutability, generators, imports, packaging. |
| Scenario and troubleshooting | 26 | You can diagnose a broken script in a defensible order. |
Difficulty inside each tier still rises as you go, so if a section starts feeling easy near its end, that is the signal to move on. Every answer names the mechanism before the command, because Python interview questions are graded on both halves: the right call or flag, and the right explanation of why the interpreter behaves that way.
Beginner Python interview questions
These 38 are the warm-up tier of Python interview questions. They look easy, and that is the trap: interviewers use them to check precision, not knowledge. Answer each one in one or two sentences, then add exactly one supporting detail. Say more and you burn round time; say less and you sound like you memorized a list. Work them in order, because difficulty climbs gently toward the intermediate tier.
What is Python, and why does ops tooling lean on it so heavily?
Answer: Python is a general-purpose interpreted language whose readability and enormous standard library made it the default glue language of infrastructure: Ansible, every major cloud SDK, and countless internal scripts are written in it. Code stays close to plain English and the interpreter handles memory for you. That pairing of low ceremony and reach is the expected core of this answer. It is also the framing behind most beginner Python interview questions: they test working fluency, not history.
How do you run Python code: the REPL, a script, and python -c?
Answer: The REPL, started with python3, evaluates expressions interactively and is the fastest way to check behavior. python3 script.py runs a file top to bottom. python3 -c "expression" runs one line from the shell, ideal inside pipelines. The detail worth adding: python3 -m module executes a module as a script, which is how you correctly invoke pip, venv, and cProfile against the exact interpreter you mean to use.
Which basic data types do you use every day in Python?
Answer: Integers, which are arbitrary precision and never overflow like C ints; floats, which are IEEE 754 doubles and still need decimal for money; strings, which are immutable Unicode; booleans; and None for no value. The precision check inside this question is immutability: knowing that ints, floats, strings, and tuples cannot change in place predicts half the behaviors interviewers probe later.
What is the difference between a list and a tuple?
Answer: A list is a mutable, ordered sequence: append, remove, and reorder after creation. A tuple is immutable: fixed once built, which makes it hashable and usable as a dict key, and signals that the shape will not change. Tuples are also slightly lighter and faster. The expected judgment, lists for collections you grow and tuples for fixed records, is exactly the layer Python interview questions grade here.
What is a dictionary, and when do you reach for one?
Answer: A dict maps keys to values with roughly constant-time lookup, backed by a hash table. Keys must be hashable, so strings, numbers, and tuples of hashables work while lists do not. Reach for one whenever you catch yourself scanning a list by a field: a users_by_id dict beats a linear search in a loop. Since Python 3.7, insertion order is preserved, which is a favorite precision check in Python interview questions.
What is a set, and what problems does it solve?
Answer: A set is an unordered collection of unique, hashable items with constant-time membership tests. It collapses duplicates automatically and supports the math operations: union, intersection, difference. The classic moves are set(a) & set(b) for common items instead of nested loops, and set(lines) to dedupe a file. Mention frozenset as the immutable variant, and name the tradeoff: you give up order.
What does None mean, and how do you check for it?
Answer: None is the singleton meaning no value: unset results, missing keys, the return of a function without return. Check it with is None, not == None, because is compares identity against the one singleton and cannot be fooled by a strange __eq__. The “my function returns nothing” confusion almost always traces to forgetting that implicit None, and it is a quiet confidence filter in Python interview questions.
What is truthiness, and which values are falsy?
Answer: Python evaluates values in boolean contexts by rule: 0, 0.0, "", [], {}, set(), None, and False are falsy; nearly everything else is truthy. So if items: reads as if the list is non-empty. The caveat that earns credit: if x: is not the same as if x is not None: when 0 or "" are legitimate values. That distinction breaks real scripts, which is why it keeps being asked.
What is the difference between == and is?
Answer: == asks whether two objects are equal by value and delegates to their __eq__; is asks whether they are the same object in memory. a = [1, 2] and b = [1, 2] are equal but not identical. Small ints and short strings are interned by the interpreter, which is why is sometimes appears to work and then fails on bigger values. The rule interviewers want: is for None and sentinels, == for everything else.
How do indexing and slicing work?
Answer: Sequences index from zero, and negative indices count from the end, so items[-1] is the last element. Slices like items[1:4] take a half-open range, start included and stop excluded, and they never raise on bad bounds; they clip instead. A step works too: items[::-1] reverses. The subtle point worth stating: a slice returns a new list, a shallow copy, which matters when the elements themselves are mutable.
What is the difference between append and extend?
Answer: append(x) adds x as a single element even when x is a list, producing a nested list. extend(iterable) adds each element, flattening one level; += does the same. Mixing them up is a leading source of surprising data shapes in scripts. Related precision point: list.sort() sorts in place and returns None, so writing items = items.sort() silently destroys your list, one of the oldest traps in Python interview questions.
How do sorted() and list.sort() differ?
Answer: sorted(iterable) accepts any iterable and returns a new sorted list, leaving the original untouched. list.sort() sorts in place and returns None. Both accept key= for custom ordering, like key=len or key=lambda r: r["age"], and reverse=True. Both are stable: ties keep their original relative order, which enables multi-pass sorting tricks. The stable-sort guarantee is the detail that separates rehearsed answers from memorized ones.
What is an f-string, and why did it replace the older formats?
Answer: An f-string embeds expressions directly in the literal: f"{host}:{port}". It displaced % formatting and .format() because it is more readable, faster, supports expressions, formatting specs like f"{avg:.2f}", and the debug form f"{value=}". It is the default choice for log lines, messages, and shell output in modern code, and using it unprompted is a small fluency signal in Python interview questions.
How do you write a function, and what does return do?
Answer: def name(params): defines a function whose body runs when called. return sends a value back and exits; no return, or a bare return, yields None. Returning several values is really returning one tuple that callers unpack: return ok, data. Functions are first-class objects you can pass around and store, which is the seed of decorators and callbacks, and interviewers like hearing that connection made early.
What are positional and keyword arguments?
Answer: Positional arguments bind by order; keyword arguments bind by name, so resize(img, width=800) is self-documenting and order-proof. Defaults live in the signature. Two refinements worth adding: parameters after a bare * become keyword-only, forcing callers to name them, and mutable defaults are a famous trap that this bank returns to in the scenario tier. Knowing the calling conventions cold is the whole point of the question.
What is a module, and how does import work?
Answer: A module is any .py file; a package is a directory of them. import json searches sys.path, executes the file once, caches it in sys.modules, and binds the name. from module import name pulls specific pieces and as renames. The useful detail: imports run code, so import has a cost and side effects fire exactly once per process, which is why library files guard their script behavior with if __name__ == "__main__":.
What does if __name__ == “__main__”: do?
Answer: When a file runs as a script, Python sets its __name__ to "__main__"; when it is imported, __name__ is the module’s name. The guard makes a file dual-purpose: reusable as a library when imported, runnable as a tool when executed. CLI entry points, Flask apps, and every well-behaved module use this pattern, and explaining both cases cleanly is what Python interview questions here are listening for.
What is pip, and how do you use it well?
Answer: pip installs packages from PyPI and their dependencies: pip install requests, pip install "requests==2.32.0" to pin, pip uninstall, and pip freeze to dump exact versions. Two professional habits: invoke it as python3 -m pip so packages land in the interpreter you actually run, and do it inside a virtualenv so you never pollute system Python. Those two habits preempt a whole family of Python interview questions about broken environments.
What is a virtual environment, and why does everyone insist on one?
Answer: A venv is a lightweight directory holding its own interpreter link and its own site-packages: create with python3 -m venv .venv, activate with source .venv/bin/activate, exit with deactivate. Installs stay isolated per project instead of colliding in system Python, which modern distros now actively protect. Naming that protection, PEP 668’s externally-managed-environment marker, shows 2026 practice rather than 2019 habits, and it is the follow-up behind many Python interview questions about installs failing on fresh machines.
What is the shebang line, and how do you make a script directly executable?
Answer: The first line, #!/usr/bin/env python3, tells the kernel which interpreter to use when the file is executed directly; chmod +x script.py then lets you run ./script.py without typing python. Using env resolves python3 from PATH, which keeps the script portable across machines with different install locations. Worth one sentence: Windows ignores shebangs and relies on the py launcher, so portable scripts do not depend on the shebang for correctness there.
How do you read a file in Python?
Answer: The pattern is with open("log.txt") as f: followed by for line in f:. The with block closes the handle even when an exception fires. Iterating the file object streams line by line, so memory stays flat regardless of file size, while f.read() and f.readlines() slurp everything and belong to small files only. Naming that memory consequence is the sentence that separates this answer from a memorized incantation, and it foreshadows a classic scenario Python interview questions love.
How do you write and append to a file?
Answer: open(path, "w") truncates and writes, "a" appends, "x" fails if the file exists, and adding "b" selects binary mode. Always pair writes with with open(...) as f: so buffers flush and the descriptor closes. The caveat to volunteer: "w" destroys existing content the moment it opens, which is how scripts wipe configs; for anything important, write to a temp file and rename atomically. Saying that unprompted reads as operational experience.
What is an exception, and what does try/except do?
Answer: Exceptions are objects raised when an operation fails; try/except catches them so the program can respond instead of dying with a traceback. except ValueError as e: catches that type, multiple except clauses are tested in order, and a bare except: catches everything including KeyboardInterrupt, which is why you avoid it. Uncaught exceptions propagate up the call stack until something handles them or the interpreter exits nonzero. That propagation mechanic is the hinge for later exception Python interview questions.
What is the difference between a syntax error and an exception?
Answer: A SyntaxError surfaces when Python parses the file: the program never starts and the message points at the offending line. An exception occurs at runtime in otherwise valid code: a KeyError on a missing dict key, a FileNotFoundError on a bad path. The operational difference matters in CI: syntax errors fail instantly for every input, while exceptions depend on the data and the path taken, which is why they hide until production shapes show up.
What are comments and docstrings, and when do you use each?
Answer: Comments, starting with #, explain why the code does what it does, for the next person editing it. Docstrings, a """...""" string as the first statement of a function, class, or module, document the contract for users, and tooling reads them: help(), IDEs, and doc generators. The expected judgment: comments for non-obvious reasoning, docstrings for interfaces, and neither for narrating code that is already clear. Clean intent here reads as team experience.
What is PEP 8, and how strictly should you follow it?
Answer: PEP 8 is the community style guide: four-space indents, snake_case for functions and variables, CapWords for classes, sane line lengths, ordered imports. Teams enforce it mechanically with formatters and linters such as black and ruff rather than by eye, which is the modern answer interviewers expect. The guide itself allows deviations with good reason and team agreement, and quoting that nuance is a small credibility win in Python interview questions about craft.
What is the standard library, and which modules do you actually use?
Answer: The standard library ships with the interpreter: json, os, sys, subprocess, pathlib, re, datetime, logging, argparse, collections, itertools, and dozens more. Batteries included is the phrase interviewers want: most glue work needs no third-party install. A strong answer to this staple of Python interview questions names the modules you personally reach for in ops scripts and what each one replaced, because it demonstrates lived fluency instead of a memorized list.
How do you work with JSON in Python?
Answer: json.loads(text) parses a JSON string into Python objects and json.dumps(obj) serializes back; json.load and json.dump do the same directly on file handles. Dicts, lists, strings, numbers, booleans, and None map cleanly; tuples become arrays, and arbitrary objects need a default= hook. Use indent=2 for humans and compact output for machines. API responses and config files make this the most-used parser in ops work and a guaranteed early question.
How do scripts accept command-line arguments?
Answer: sys.argv is the raw list: argv[0] is the script name and the rest arrive as strings. For anything beyond one flag, argparse gives typed, documented options and generates --help for free. Two details worth adding: every argv value is a string, so numeric flags need type=int, and hand-rolled argv parsing in real tools is the smell this family of Python interview questions is fishing to expose.
What is the difference between a for loop and a while loop?
Answer: A for loop iterates over anything iterable, lists, dicts, files, generators, and ends when the iterable is exhausted. A while loop repeats until its condition turns false, which is the right shape for unbounded waits like polling a service until healthy. Mention break, continue, and the little-known else clause on loops, which runs only when no break fired; knowing it exists signals depth beyond the tutorials.
How do you iterate a dictionary, and what does .items() give you?
Answer: Iterating a dict yields its keys; .values() gives values and .items() yields key-value tuples you unpack directly: for key, val in d.items():. Insertion order is preserved in modern Python. The classic precision check: mutating a dict while iterating it raises RuntimeError, so iterate over a snapshot like list(d.items()) when removing entries. That exact bug returns later as one of the scenario Python interview questions, so state the rule now.
What is string immutability, and why does it matter?
Answer: Strings cannot change in place; every modification builds a new string object. s.upper() returns a new string and silently does nothing if you discard the result, the most common beginner string bug. The performance consequence: growing a big string with += in a loop copies repeatedly, so you collect parts in a list and call "".join(parts) at the end. That gotcha returns in the performance entries of intermediate Python interview questions, so plant it early.
What is a list comprehension?
Answer: A comprehension builds a list inline: [x**2 for x in nums if x > 0], replacing the append-in-a-loop pattern with one readable expression. Comprehensions also exist for dicts and sets, using braces. The judgment point: use them to transform data, not for side effects; a comprehension whose result is thrown away is a loop wearing a costume. Anything nested more than one level deep usually reads worse than the plain loop it replaced.
What does zip() do, and what is it good for?
Answer: zip(a, b) pairs elements of two iterables position by position, which is how you turn parallel lists into records or build a dict with dict(zip(keys, values)). It stops at the shorter iterable unless you use itertools.zip_longest. The sharp addition: zip(a, b, strict=True), available since 3.10, raises on length mismatch instead of silently dropping leftovers, a real data-loss bug in pipelines that has earned this question a permanent spot in Python interview questions.
What does enumerate() do, and why prefer it over range(len())?
Answer: enumerate(items) yields index-value pairs, replacing the C-style for i in range(len(items)) pattern with direct iteration. A start offset is built in: enumerate(items, start=1) for human-numbered output. It is cleaner, cannot desynchronize the index from the value, and reads as idiomatic. Interviewers read range(len(...)) as a habit translated from another language, which is precisely the signal this small question exists to catch.
What is the difference between a local and a global variable?
Answer: Names assigned inside a function are local to each call; module-level names are global and readable anywhere in that module. Assigning to a global inside a function requires the global statement, otherwise Python creates a new local, which is the source of UnboundLocalError surprises. The judgment interviewers want stated: pass values in and return values out, because scripts that lean on globals become untestable and untrustworthy in pipelines.
What is the with statement, conceptually?
Answer: with wraps a block in guaranteed setup and teardown: the object’s __enter__ runs at entry, __exit__ runs on exit whether the block finished or raised. Files close, locks release, temporary directories clean up. This is the general shape called a context manager, and it replaces hand-written cleanup in finally. Keep this conceptual answer polished; the intermediate tier of Python interview questions goes one protocol level deeper on it.
How do you check which Python version a script is running under?
Answer: In the shell, python3 --version. Inside a script, sys.version_info gives a tuple you can compare: if sys.version_info >= (3, 10):. Scripts that use modern syntax should fail fast with a clear message instead of a SyntaxError far from the real cause. The sentence that lands: the interpreter that happens to run the script is not always the one you configured, and that gap is the root of many environment Python interview questions.
Intermediate Python interview questions
This tier is where Python interview questions stop testing vocabulary and start testing mechanism. Forty questions follow, and the pattern to internalize is behavior, then cause: name what the interpreter actually does, then explain why. That second half is what moves you from scripter to engineer in an interviewer’s notes. The concurrency cluster, the GIL, threads, processes, asyncio, and the packaging cluster, pyproject, wheels, locks, uv, carry the most weight in DevOps-flavored Python interview questions, so rehearse those first.
What is the difference between a shallow copy and a deep copy?
Answer: Assignment copies the reference, not the object: b = a leaves one list with two names. a.copy(), a[:], or copy.copy(a) makes a shallow copy: a new outer container whose elements are still shared references, so nested mutation bleeds across. copy.deepcopy(a) recursively copies everything. Choose per data shape and do not blanket-apply deepcopy, it is slow. Most aliasing bug reports reduce to one sentence: I thought I had a copy.
Why must dict keys be hashable, and what is hashability?
Answer: Hashable objects work with hash() and stay equal to themselves for their lifetime. A dict hashes each key to locate its slot, so a key that could mutate after insertion could move to a different slot and corrupt lookup; the language forbids it. Strings, numbers, and tuples of hashables qualify; lists, dicts, and sets do not, and neither does a tuple containing a list. This is why record keys are tuples, not lists, a distinction intermediate Python interview questions check early.
How does Python manage memory?
Answer: CPython reference-counts every object, and when a count hits zero the object is freed immediately; a separate cycle collector reclaims reference cycles that counting alone cannot. Consequences you can state: del removes a name, not an object; freed memory returns to Python’s allocator, not always to the OS; and long-lived caches grow until you bound them. Leak hunts in Python usually end at a growing container holding references, not at the allocator itself.
What is the GIL, and what are its consequences?
Answer: The Global Interpreter Lock lets only one thread execute Python bytecode at a time within a CPython process. It simplifies the interpreter and keeps single-threaded code fast, but it means CPU-bound threads never run in parallel. The lock is released during blocking I/O and inside many C extensions such as numpy, so threads still help I/O-bound work. Free-threaded builds are arriving, but the default interpreter you will be interviewed on in 2026 still has the GIL, and Python interview questions expect you to reason from it.
When do threads actually help a Python program?
Answer: For I/O-bound work: HTTP calls, database queries, socket and disk waits, because a thread releases the GIL while it blocks and the others proceed. concurrent.futures.ThreadPoolExecutor with executor.map is the standard shape. For CPU-bound loops threads gain nothing and add contention. Caveats to name: shared mutable state needs locks, and exceptions inside workers surface only when you collect results, so check futures even when the code looks fine.
When do you choose multiprocessing over threading?
Answer: When the work is CPU-bound. multiprocessing and ProcessPoolExecutor spawn real processes, each with its own interpreter and GIL, so computation genuinely parallelizes across cores. The costs: arguments and results cross process boundaries by pickling, startup is heavier, and shared state needs explicit machinery. The one-line judgment, threads for waiting, processes for computing, is the sentence interviewers wait for, and it anchors the concurrency block of Python interview questions at this level.
What is asyncio, and which workloads suit it?
Answer: asyncio is single-threaded cooperative concurrency: tasks explicitly await, yielding control at I/O boundaries, so one event loop interleaves thousands of sockets without thread overhead. It shines for many concurrent network operations and does nothing for CPU-bound work, which blocks the loop and stalls every task. One blocking call, a raw requests.get, inside async code is the classic outage. Use asyncio.gather to fan out and async-aware libraries like aiohttp or httpx.
What is a generator, and how does yield work?
Answer: A generator function contains yield: calling it returns a generator object that produces one value per next(), freezing and resuming its local state between calls. Nothing executes until the first iteration, and memory stays constant regardless of result count because nothing is materialized. That is why log parsers, file readers, and infinite sequences are written as generators. They are single-use: once exhausted, a generator stays empty, a silent bug when scripts reuse handles. This mechanism powers a full cluster of intermediate Python interview questions.
What is the difference between a list comprehension and a generator expression?
Answer: A list comprehension materializes the whole result now: [parse(line) for line in handle] holds everything in memory. Swap the brackets for parentheses and you have a generator expression: lazy, constant memory, single-use, ideal inside sum(), any(), or another consumer that may short-circuit. The rule: comprehension when you need the collection more than once, generator expression when values flow through once. On large inputs the memory difference is gigabytes versus kilobytes, which is why this pairing recurs in Python interview questions about scale.
What is a decorator, and how do you write one?
Answer: A decorator is a callable that takes a function and returns a replacement, usually a wrapper that does work before and after calling the original: @timer above def run(). They exist because functions are first-class objects you can pass and return. The skeleton to know cold: define an outer function taking fn, an inner wrapper(*args, **kwargs) that calls fn(*args, **kwargs), and return the wrapper. Uses: timing, retries, caching with functools.cache, registration. Being able to write that skeleton live is the real test inside decorator Python interview questions.
Why do decorators need functools.wraps?
Answer: The wrapper replaces the original function object, so __name__, __doc__, and signature metadata now describe wrapper, breaking help(), debuggers, and anything introspecting the API. @functools.wraps(fn) on the wrapper copies the original metadata back, one line, no cost. Interviewers treat forgetting it as evidence you have written decorators but never debugged the confusing tracebacks they produce, which is exactly what this question is calibrated to detect.
What do *args and **kwargs do?
Answer: In a signature, *args collects extra positional arguments into a tuple and **kwargs collects extra keyword arguments into a dict. At call sites the same stars unpack: fn(*arg_list, **kw_dict) forwards everything. Together they let decorators and wrappers accept and forward arbitrary calls. Keyword-only parameters declared after *args stay explicit even in flexible APIs. Fluency in both directions, packing and unpacking, is the checkpoint this question grades.
What is a closure, and where does it bite?
Answer: An inner function that references names from an enclosing function captures those names, forming a closure; the captured environment lives as long as the inner function does. The famous bite: closures capture names, not values, so [lambda: i for i in range(3)] yields three lambdas that all see the final value of i. Bind at definition with a default argument, lambda i=i: i, to freeze the value. Loop-created callbacks carrying this bug are a reliable follow-up in intermediate Python interview questions.
What is a context manager, and how do you write one?
Answer: A context manager is any object with __enter__ and __exit__: with obj: calls enter, guarantees exit on success or exception, and __exit__ receives exception details so it can clean up or suppress. contextlib.contextmanager builds one from a generator with a single yield, with cleanup after the yield. Files, locks, database connections, and tempfile.TemporaryDirectory all speak this protocol. The operational point, cleanup that survives exceptions, is the whole reason the protocol exists and the sentence Python interview questions here want to hear.
How is the exception hierarchy organized, and why does except order matter?
Answer: Exceptions form a tree under BaseException; ordinary failures live under Exception, with families like OSError whose subclasses include FileNotFoundError and PermissionError. Except clauses match in order, first hit wins, so specific subclasses must precede their parents; putting except Exception first makes every later branch dead code. Custom exceptions subclass Exception so tooling treats them as ordinary failures. Naming the tree and the ordering rule is the complete answer.
What does raise … from do?
Answer: raise ConfigError(msg) from err chains exceptions: the traceback shows both, the original cause and your translation, instead of the default during handling of the above exception, another occurred chain you get when raising inside an except block. Use it to convert a low-level error into your domain vocabulary while preserving the cause for logs. from None suppresses the chain deliberately for clean user-facing errors. Chain or suppress consciously; accidental chains are noise, and that judgment is what the question grades.
What is EAFP, and why is it considered Pythonic?
Answer: EAFP, easier to ask forgiveness than permission, means attempting the operation and catching the exception instead of pre-checking conditions, the style called LBYL, look before you leap. try: value = cfg[key] with except KeyError versus if key in cfg:. Python leans EAFP because checks and actions can race, and because exceptions are cheap when failures are rare. In filesystem and concurrency code, EAFP avoids check-then-act windows entirely. Give both acronyms and one race example and the answer is complete.
What are dataclasses, and what do they replace?
Answer: @dataclass generates __init__, __repr__, and __eq__ from annotated class fields, replacing boilerplate value objects. Options worth naming: frozen=True for immutable, hashable instances, field(default_factory=list) as the safe way to give a list default, and slots=True for leaner memory. They beat plain dicts for structured records because attribute access is checked and typos fail loudly at once. Mention that they displaced namedtuple in most code and this answer closes cleanly.
What are type hints, and do they do anything at runtime?
Answer: Annotations like def deploy(host: str, port: int = 22) -> bool: document expected types for humans and tools; the interpreter ignores them at runtime by default. typing adds generics such as list[str], Optional[X], and TypedDict. The value is IDE completion, safer refactors, and executable documentation. Answer the trap directly: passing wrong types does not raise by itself; enforcement is a separate static step, which is exactly what the next question covers and what Python interview questions in typed codebases expect you to know.
What is mypy, and how does it fit a team workflow?
Answer: mypy, or pyright, statically checks annotations without running code, catching type errors in CI the way a compiler would in stricter languages. Teams run it on every pull request, start permissive with --ignore-missing-imports, and tighten gradually, because gradual typing means untyped code is simply not checked yet. The closing sentence interviewers like: static types and tests catch different failure classes, so they complement rather than replace each other.
What is the iterator protocol?
Answer: An iterable implements __iter__ returning an iterator; an iterator implements __next__ and raises StopIteration when exhausted. The for statement calls iter() once and next() repeatedly, which is why anything iterable works in a loop. Generators are iterators assembled for free from functions. The operational consequence: iterators are consumed once, so iterating an exhausted generator or an already-read file handle yields nothing, silently, a real bug shape in scripts that reuse handles.
How is a dict implemented, and is it ordered?
Answer: A dict is a hash table: the key’s hash selects a slot, collisions are probed, and the table resizes as it fills to keep operations cheap. Since 3.7, insertion order is a language guarantee, implemented with a compact entries layout. Average lookup, insert, and delete are constant time; worst-case degradation needs pathological collisions you will not hit with ordinary strings. Knowing that lookups are O(1) is what turns dict-versus-list interview answers from opinion into arithmetic.
Why is set membership faster than list membership?
Answer: x in items on a list scans linearly, O(n); on a set it hashes, O(1) on average. A loop over n records checking membership in m entries costs O(n*m) with a list and O(n+m) with a set, which is the difference between a script that finishes in seconds and one that runs overnight. Convert once, allowed = set(config_list), before the loop. This is one of the most practical Python interview questions in the bank because the fix is one line and the win is often three orders of magnitude.
Why is “”.join(parts) faster than += in a loop?
Answer: Strings are immutable, so s += part conceptually builds a new string each pass, making loop concatenation quadratic in total length. "".join(parts) measures the total once and copies once. CPython optimizes some in-place += cases, but only under conditions you should never rely on in production. State the immutability cause, not just the benchmark numbers: interviewers grade the mechanism, and this one anchors the performance cluster of Python interview questions.
How do you run an external command with subprocess correctly?
Answer: The default shape is subprocess.run(["git", "status"], capture_output=True, text=True, check=True): arguments as a list so nothing is shell-parsed, output captured as strings, and non-zero exits raising CalledProcessError instead of failing silently. Add timeout= to bound runaway commands. Read result.stdout, result.stderr, and result.returncode when you handle failures yourself. Popen exists for streaming and interactive control, but run() covers nearly all ops scripting, and forgetting check=True is the error this question hunts.
Why is shell=True dangerous in subprocess calls?
Answer: shell=True hands the command string to /bin/sh, which enables pipes and globs but also injection: anything interpolated into that string executes as shell code. run(f"ls {user_input}", shell=True) with input like ; rm -rf ~ is the canonical disaster. Default to the list form and shell=False; when you genuinely need a pipeline, chain run() calls or reach for shlex.join when logging commands. Explaining the injection vector precisely is the graded half of this entry in Python interview questions.
How do you capture a subprocess’s output without deadlocking?
Answer: If you launch with Popen, capture both stdout and stderr as pipes, and read them sequentially, the child can fill one pipe’s buffer, about 64 KB on Linux, and block on writing while you block reading the other: a deadlock. communicate() drains both pipes concurrently, and run(capture_output=True) uses it internally. Manual alternatives are threads or routing one stream to a file. Name the buffer-size mechanism and the fix follows logically, which is exactly the chain this question tests.
What is pathlib, and why prefer it over os.path?
Answer: pathlib.Path treats paths as objects: Path("/var/log") / "app.log" joins correctly across platforms, and methods like .exists(), .read_text(), .glob("*.log"), .resolve(), and .with_suffix(".bak") replace string surgery with named operations. Paths compare semantically and compose with open() directly. Teams migrate because whole classes of bugs, trailing separators, wrong separators, manual basename logic, stop being possible. Mention os.path still exists for legacy APIs and that Path accepts both styles.
What does argparse give you over hand-parsed arguments?
Answer: argparse builds CLIs declaratively: an ArgumentParser, add_argument("--verbose", action="store_true"), add_argument("config", type=Path), and it generates --help, validates types, and exits with code 2 on misuse. Subparsers via add_subparsers() model git-style command trees. The professional detail interviewers wait for: pair argparse with explicit exit codes and parser.error() messages so scripts behave correctly inside CI jobs and shell pipelines. Hand-rolled argv parsing fails exactly there, which is the point of the question.
Why use logging instead of print in production scripts?
Answer: The logging module separates emitting from delivering: loggers name components, handlers choose destinations like stderr, files, or syslog, formatters shape lines, and levels from DEBUG to CRITICAL filter at runtime. print cannot be routed, silenced, or enriched, which is why it does not survive contact with operations. Setup is one logging.basicConfig(level=..., format=...) call at startup. The marked detail: lazy %s-style formatting defers string building until the level is enabled, and interviewers notice when you know that about logging-focused Python interview questions.
How do imports resolve, and what is sys.path?
Answer: sys.path is the ordered search list the import system walks: the script’s directory first, then PYTHONPATH, then the site-packages of the active environment. Imported modules execute once and cache in sys.modules. ModuleNotFoundError almost always means the module is not on this interpreter’s path: wrong venv, wrong interpreter, or a package installed elsewhere. python3 -m pip ties installs to the running interpreter, and printing sys.executable ends most environment arguments on the spot.
What is the difference between pyproject.toml and requirements.txt?
Answer: pyproject.toml is the standard project file: metadata, dependencies as compatible ranges, and build backend configuration. requirements.txt is an application-oriented flat list, typically fully pinned output from pip freeze or pip-compile. Libraries declare ranges in pyproject so consumers can resolve; deployed applications pin exact versions so builds are reproducible. Knowing which file owns which job, and refusing to mix them up, is the working answer to this packaging staple of Python interview questions.
What is a wheel, and why is it preferred over an sdist?
Answer: A wheel is a prebuilt install archive: pip unpacks it without compiling, which makes installs fast and dependency-light. An sdist ships source that pip must build at install time, requiring compilers and build backends. Prefer wheels when they exist and enforce it in CI with pip install --only-binary :all:. Some packages publish only sdists, which is exactly why a fresh container build suddenly needs gcc. Naming the build-time cost is the insight this question rewards.
What is a lock file, and when do you commit one?
Answer: A lock file pins the entire resolved graph: every package at an exact version with hashes, so every install is reproducible byte for byte. pip-compile generates pinned requirements from pyproject ranges; uv lock, Poetry’s lock, and the newer PEP 751 format are the modern forms. Commit locks for applications and services, where reproducible deploys matter; do not commit them for libraries, where consumers resolve their own graphs. That split is the entire judgment this question exists to test.
What is uv, and what problem does it solve?
Answer: uv is a Rust-based Python package and environment manager: it resolves and installs orders of magnitude faster than pip, manages venvs and Python versions, and handles project workflows like uv sync against a lockfile, with a pip-compatible interface via uv pip install. The adoption logic is honest: same artifacts, far less waiting, one static binary instead of pip plus virtualenv plus pyenv. Concede the caveat, it is younger than pip, while noting its compatibility layer is broad and many 2026 shops treat it as default, which is why it now appears in Python interview questions.
What are pytest fixtures, and why are they powerful?
Answer: Fixtures are declared test dependencies: a function marked @pytest.fixture provides setup, tests request it by naming it as a parameter, and pytest injects it. They compose, they scope per function, module, or session, and cleanup after a yield replaces teardown methods. Compared with xUnit-style setUp, fixtures are explicit, reusable, and composable, which is a large part of why pytest displaced unittest. Built-ins like tmp_path show the payoff: real temp directories with zero boilerplate.
How do you fake external calls in tests?
Answer: With unittest.mock.patch, pytest’s monkeypatch fixture, or pytest-mock’s mocker: swap the real function for a fake during the test so you never hit APIs, clocks, or the filesystem. The rule people get wrong: patch where the name is looked up, not where it is defined. Assert interactions with assert_called_once_with, keep fakes minimal, and mock boundaries, not internals, because over-mocking tests your mocks rather than your code. That boundary discipline is the depth marker in testing-flavored Python interview questions.
What is __pycache__, and can you delete it?
Answer: On first import, CPython compiles source to bytecode and caches it as .pyc files under __pycache__, keyed by source modification time; later imports skip compilation. It is a pure speed cache: deleting it is always safe and stale caches self-invalidate when sources change. Deployment nuance worth adding: read-only containers should ship precompiled bytecode via compileall, and PYTHONDONTWRITEBYTECODE=1 silences write attempts in awkward filesystems. Explaining the mtime key is the depth marker here.
How do you handle signals for graceful shutdown in a long-running script?
Answer: Register a handler with signal.signal(signal.SIGTERM, handler), or loop.add_signal_handler under asyncio, that sets a stop flag or stops the loop so workers finish and resources close through finally blocks. The default behavior dies mid-write, which is how partial uploads and orphaned temp files happen. In containers this is not exotic: docker stop sends exactly SIGTERM with a short grace period before SIGKILL, and a supervisor that ignores it ships broken shutdowns.
What do cProfile’s columns mean, and which do you read first?
Answer: python3 -m cProfile -s cumtime script.py instruments every call. tottime is time spent in the function itself, excluding callees; cumtime includes everything it called; ncalls exposes functions invoked far too often. Sort by tottime to find hot code and by cumtime to find expensive subtrees. It adds overhead and distorts very fast calls, so profile representative workloads. For live production processes, sampling profilers like py-spy attach without stopping anything, and naming that tool is now standard in performance Python interview questions.
Scenario and troubleshooting Python interview questions
This tier is how scenario rounds actually feel: one symptom sentence, then the interviewer goes quiet while you think out loud. For every item below, the graded skill is identical: state your order of checks and the reasoning behind it, then act. Silence plus random patching loses to a spoken diagnostic plan almost every time, and that plan is what these scenario Python interview questions really measure. Rehearse the six classics first: the mutable default, the memory blowup, the thread race, the dependency conflict, the slow script, and the subprocess deadlock.
A function with a list default argument keeps old data between calls. What is happening?
Answer: That is the mutable default trap: default values are evaluated once at function definition, so every call that omits the argument shares the same list object. Diagnostic order: reproduce with two calls, print id(items) to show the same object, then point at func.__defaults__ for proof. The fix is a None sentinel: def add(task, items=None): items = [] if items is None else items. Interviewers want the mechanism, definition-time evaluation, not just the patch, and this is the single most famous of all Python interview questions.
A script reading a large log file balloons to the file’s size in RAM. What do you check?
Answer: Suspect a slurp first: f.read(), f.readlines(), or a list comprehension over the file handle materializes the whole file. Diagnostic order: confirm RSS with /usr/bin/time -v, locate the slurping line, then convert to streaming: iterate the handle directly with for line in f:, or restructure multi-pass logic into a generator. For pandas, the same disease needs chunksize=. Verify the fix by watching max RSS drop. Streaming is the entire answer, and it is one of the most common scenario Python interview questions in ops loops.
Four threads increment a shared counter, and the total comes up short. Why?
Answer: A lost update: counter += 1 is read-modify-write, and with the GIL released between bytecodes, threads interleave so two threads read the same value and one increment vanishes. Diagnostic order: run it in a loop to show the shortfall varies, then explain the race window explicitly. Fix with threading.Lock() around the mutation, or restructure with queue.Queue and a single writer. The race is the mechanism, the lock is the cure, and walking that chain calmly is what scenario Python interview questions reward here.
Installing package B breaks package A with a dependency conflict. What is your play?
Answer: Order: read the resolver’s conflict message, run pip check to see the broken edge, and inspect both packages’ declared ranges with pip show. Then decide in order of preference: pin a shared version that satisfies both, add a constraints file so one library holds still while others move, or isolate the incompatible dependency in its own venv and call it as a subprocess. Fork or vendor is the last resort. Dependency conflicts are among the most frequently practiced scenario Python interview questions because every real project hits one.
A data pipeline that took seconds now takes an hour. Where do you start?
Answer: Measure before touching anything: python3 -m cProfile -s cumtime pipeline.py and read tottime for the hottest function. The usual culprits in ops scripts: O(n squared) membership checks against a list that should be a set, re-reading the same file inside a loop, one subprocess invocation per item where one batched call would do, and DNS or sleep calls inside tight loops. Confirm each fix with timeit on representative input. Profiling beats guessing, and naming the column you read first is the detail graders listen for in slow-script Python interview questions.
A subprocess call hangs forever, but the same command works in a terminal. What is going on?
Answer: Pipe deadlock. You launched with Popen, captured stdout and stderr as pipes, and read them one after the other; the child filled one pipe’s buffer, about 64 KB, and blocked on writing while you blocked reading the other. Diagnostic order: confirm the parent’s stack with py-spy dump showing a blocked read(), then fix with communicate() or run(capture_output=True), or route one stream to a file. Add timeout= so the next occurrence fails loudly instead of hanging a pipeline overnight. The buffer mechanics are the whole story, and deadlocks of this kind are a staple of scenario Python interview questions.
pip list shows the package, but the import still fails. Walk me through it.
Answer: First command, always: python3 -c "import sys; print(sys.executable)", because the interpreter that ran is rarely the one pip installed into. Then check which python3, whether VIRTUAL_ENV is set, and pip show package for the install location. In cron and CI, activate the venv or call the absolute interpreter path .venv/bin/python. The rule to state: never diagnose an import error by reinstalling; diagnose by matching interpreter to site-packages. This single scenario separates people who have operated Python in production from those who have only written it, which is why it anchors environment-focused Python interview questions.
The script works on your machine and fails in CI. Give me your order of checks.
Answer: Diff the interpreters first: local python3 --version versus the CI image’s. Then the environments: env vars your script reads silently, working directory, and locale-dependent encoding defaults. Then the packages: without a lock file, CI resolves fresh versions you never tested. Reproduce inside the CI image locally with docker run and the same entrypoint before changing code. Fix by pinning the interpreter, committing the lock file, and failing fast on missing env vars. The method is the answer, and it is the framing most CI-flavored Python interview questions expect.
Reading a vendor CSV raises UnicodeDecodeError. What do you do?
Answer: The message names a byte the UTF-8 decoder rejects, which means the file is not UTF-8. Order: identify the real encoding with file -i or the charsetdetect libraries, then pick a policy: open(path, encoding="cp1252") when the source is legacy Windows, errors="replace" for logs you only grep, or normalize at the boundary and store UTF-8 onward. Never fix this by globally silencing decode errors; fix the declared encoding where the file enters the system, then document it. Encoding mismatches surface only across machines, which keeps this question alive in scenario rounds.
A script hangs with no output and no exit. How do you see inside it?
Answer: Attach, do not kill. py-spy dump --pid PID prints the current stack of every thread and usually ends the investigation: blocked in read(), waiting on a lock, or spinning in a loop. Without py-spy, run under faulthandler and trigger a dump, or check strace to see the syscall it is parked in. Classify before fixing: blocked I/O, a forgotten input() prompt, a lock cycle, or a CPU-spinning infinite loop. Killing and rerunning destroys the only evidence, and saying that out loud is the senior move in hang-diagnosis Python interview questions.
Deleting keys inside a for loop over a dict crashes with RuntimeError. Fix it properly.
Answer: The dict iterator detects size changes and raises RuntimeError: dictionary changed size during iteration rather than corrupting internal state. Order: find the mutating loop, then fix by intent: iterate a snapshot with for key in list(d):, collect doomed keys and delete after the loop, or rebuild with a dict comprehension that keeps only survivors. The same rule governs sets. Explaining why the guard exists, mutation would invalidate the iterator’s position, lifts the answer above syntax level, and it is a reliable mid-tier follow-up in Python interview questions about containers.
After running for hours, the script dies with too many open files. What happened?
Answer: Descriptor leak: OSError: [Errno 24] Too many open files after accumulation means something opens without closing. Order: count with ls /proc/PID/fd | wc -l, sample what the descriptors are, and find the pattern: files opened without with, subprocess pipes never drained, sockets held by threads you never joined. Fix the pattern with context managers everywhere; raise the soft limit with ulimit -n or a systemd LimitNOFILE only as a stopgap. Limits delay the crash, closing behavior fixes it, and stating that distinction crisply is what graders want from leak-flavored Python interview questions.
Tests pass locally and fail randomly in CI. What do you hunt first?
Answer: Order dependence. Run the suite in random order with pytest-randomly, then singly with pytest -p no:randomly -k test_name, because a test that only passes after another one runs shares state: module-level globals, files in fixed temp paths, monkeypatches never undone. Next look for time and randomness: real clocks, sleeps, and assertions over unordered sets or dicts. Fix by isolating, fixtures with teardown, frozen clocks, sorted comparisons. Flaky tests rot trust in the whole suite, and saying that sentence is what interviewers remember from flaky-test Python interview questions.
A production Python service is pinning a CPU. How do you find the hot code without restarting?
Answer: Attach a sampling profiler: py-spy top --pid PID reads live stacks without stopping the process and names the hot function within seconds. If py-spy is unavailable, check whether the service enabled faulthandler.register for a signal-dumped traceback, or pull a core for offline analysis. Common causes: retry loops without backoff, regex catastrophic backtracking, huge JSON parses, and GIL-bound loops that more threads cannot help. Then decide: fix the loop, shed load, or restart with guardrails. Naming the sampling tool first is now the expected opening in production-profiling Python interview questions.
A script makes 200 API calls sequentially and crawls. How do you speed it up?
Answer: Confirm the calls are independent, then pick the model: I/O-bound work means a ThreadPoolExecutor(max_workers=20) with executor.map is a five-line fix, and asyncio with an async HTTP client scales to thousands. Bound concurrency with a semaphore so you do not hammer the API into rate-limiting you, add retries with exponential backoff, and use executor.map when order matters. Threads beat processes here because the cost is waiting, not compute. Twenty workers turn a minute of latency into seconds, and that arithmetic is the punchline of concurrency-flavored Python interview questions.
ps fills up with defunct children from your Python script. What is wrong?
Answer: Zombies: your parent spawns with Popen and never reaps, so exited children keep process-table entries. Order: find the parent with ps -o ppid= -p ZPID, confirm it is your code, then fix the reaping: call proc.wait() or communicate(), prefer subprocess.run which reaps by design, and handle children outliving a killed parent with start_new_session=True plus a shutdown sweep. Killing the zombies is impossible; fixing the parent is the fix. The reaping mechanism is the graded knowledge in this entry of scenario Python interview questions.
pip install on a fresh distro fails with externally-managed-environment. What now?
Answer: That is PEP 668: the distro marks its system Python as externally managed so pip cannot break apt-managed tooling that shares the interpreter. Order: recognize it as protection, not breakage. Then choose: create a venv with python3 -m venv .venv for project work, use pipx for CLI tools, or containerize. --break-system-packages exists and is the move that causes the next incident. Explaining what the marker protects is the depth this scenario rewards, and it is now a standard opener in packaging-flavored Python interview questions.
After a refactor you get cannot import name from partially initialized module. What is the fix?
Answer: A circular import: module A imports B while B is still mid-import of A, so the name does not exist yet. Order: map the cycle from the traceback, then break it at the weakest link: move the shared piece into a third module both can import, defer the import into the function that needs it, or import the module instead of the name and access module.attr later. Type-annotation-only cycles dissolve under if TYPE_CHECKING:. Naming partial initialization as the cause is what turns this fix into understanding in refactoring Python interview questions.
The script exits 0 but downstream data is quietly wrong. Where do you look?
Answer: Silent failure. Order: grep the code for bare except: and overbroad except Exception: pass, both of which swallow real errors and even Ctrl-C; check paths that continue past unreadable records; verify return codes are not fabricated with sys.exit(0). Fix by policy: catch only what you handle, log what you catch with log.exception so the traceback survives, and let everything else propagate to a nonzero exit. Silent success is worse than loud failure in pipelines, and that sentence is the takeaway interviewers want from failure-handling Python interview questions.
A worker’s memory grows for days until the pod is OOMKilled. How do you hunt it?
Answer: Prove growth first with RSS over time from monitoring, then attribute: tracemalloc snapshots taken hours apart, diffed, name the lines allocating the growth; gc.get_objects() and objgraph find reference leaks and runaway caches. Usual culprits: unbounded lists used as caches, log handlers accumulating records, module-level lists appended per request, and cycles with __del__. Fix by bounding with functools.cache(maxsize=...) or cachetools LRU, and restart-with-guardrails while the fix ships. Attribution before remedy is the discipline memory-leak Python interview questions grade.
pickle.load on an old cache file now raises AttributeError. What happened and what do you do?
Answer: Pickle stores classes by module path and name, so a refactor that renamed or moved the class breaks every old pickle on load. Order: confirm the path changed, then regenerate the artifact with current code rather than shimming the old path indefinitely. For artifacts that must cross versions, switch to JSON or another explicit format, or version the payload with a schema tag. And state the security line plainly: never unpickle untrusted data, because pickle deserialization executes arbitrary code. That pairing of mechanics and risk is exactly what this question tests.
Scheduled jobs run at the wrong hour after moving to UTC servers. What is the bug?
Answer: Naive datetimes. datetime.now() and utcnow() return values with no timezone attached, so comparisons silently mix local and UTC. Order: audit for naive constructors, then fix policy: timezone-aware everywhere, datetime.now(timezone.utc), convert for display with zoneinfo, and store UTC at rest. Note that utcnow() is deprecated in modern Python for exactly this reason. The bug only surfaces when machines or users span zones, which is why timezone bugs are quiet classics in scenario Python interview questions.
A YAML config turns the value no into boolean False. What is going on?
Answer: YAML 1.1 parses unquoted no, yes, on, and off as booleans, the famous Norway problem where country code NO becomes false. Order: reproduce with yaml.safe_load, then fix at the boundary: quote the value, prefer TOML or JSON for machine-written config, or pin a loader with 1.2 core schema semantics. While you are there, confirm the code uses safe_load, because full yaml.load can construct arbitrary objects from config. Both the surprise and the safety point belong in the answer to this one.
The script works in your terminal and misbehaves under cron or systemd. Diff what?
Answer: The environment. Order: PATH first, because bare python3 may resolve differently or not at all; then the working directory, since cron starts in home and relative paths break; then environment variables your shell profile provides and the scheduler does not; then TTY absence, which changes prompts and some output buffering. Fix with the venv’s absolute interpreter path, absolute paths or explicit WorkingDirectory, an EnvironmentFile, and PYTHONUNBUFFERED=1 with logs to files. This is the most operations-flavored of scenario Python interview questions, and interviewers expect the diff order without prompting.
One specific input freezes a regex-based parser; everything else parses instantly. Why?
Answer: Catastrophic backtracking: nested quantifiers like (a+)+ or stacked wildcards make the regex engine explore exponentially many paths on near-miss input. Order: capture the offending input, test the pattern alone on it, and profile to show time piling up inside the regex engine. Fix by rewriting the pattern to remove ambiguity, restructuring into simpler sequential matches, or pre-filtering by length, and prefer the regex module’s timeouts where available. Naming the backtracking mechanism is the whole answer; the rewrite follows from it, and interviewers know it.
Your shell pipeline keeps going after the Python step failed. What is broken?
Answer: Exit codes. Order: reproduce and check echo $? after each stage, then find the swallow: an unhandled exception exits 1, but except: pass paths exit 0, and sys.exit("message") exits 1 while printing to stderr, which people mistake for success. Fix by policy: explicit exit codes per outcome, argparse errors already exit 2, and set -eo pipefail on the shell side so any stage failure stops the chain. Silent success is the most expensive failure mode in automation, and closing on that sentence is the right note for the scenario tier of Python interview questions.
Hands-on scenario labs
Reading answers builds vocabulary; reflexes take repetition, and the scenario tier of Python interview questions is graded on reflexes. Each lab below is a safe, self-inflicted incident you can run in a throwaway container with nothing but the standard library and pip. Do each one three times: once with the checklist open, once from memory, once narrating out loud as if an interviewer were listening. That narration is exactly what scenario Python interview questions reward.
Lab 1: break and fix the mutable default argument
- Write
def add_task(task, tasks=[]): tasks.append(task); return tasks, call it three times, and observe the pileup of old tasks. - Prove the mechanism: print
id(tasks)inside the function and inspectadd_task.__defaults__to see the single shared list object. - Fix it with the
Nonesentinel pattern and a fresh list per call, then write a pytest test that asserts each call starts empty. - Finish from memory, narrating definition-time evaluation as the cause and the sentinel as the cure, in under 60 seconds.
Lab 2: provoke and repair a thread race
- Write a script that runs four threads, each incrementing a shared
counter += 1100,000 times, and record how far the final total falls short. - Explain the lost update out loud: read, modify, write, with thread switches between the steps.
- Add a
threading.Lock()around the increment and confirm exact totals across ten runs, then rebuild the same pipeline withqueue.Queueand a single writer thread. - Close by pointing at the exact line that forms the race window, not just where the lock goes, and note when threads are the wrong tool entirely.
Lab 3: the venv and dependency conflict drill
- Create two environments with
python3 -m venv .venv-aand.venv-b, and install a package plus a pinned dependency in each. - Force a conflict: install an older shared dependency in one venv, then a package whose metadata requires a newer one, and run
pip checkto see the broken edge. - Repair with a constraints file,
pip install -c constraints.txt -r requirements.txt, and verify the resolver holds the pinned version. - Rebuild both environments with
uv venvanduv pip sync, time the difference, and end by proving which interpreter runs which environment withsys.executable.
Cheat sheet: the commands behind the answers
Nearly every item in this bank of Python interview questions resolves to one of the entries below, reached in the same order you would use them live. Learn the table, then attach one sentence to each row about when it is the right tool, and you can walk any debugging round without improvising.
| Command | What it tells you |
|---|---|
python3 --version | Which language version you are really running, the first fact in every environment dispute |
python3 -c "import sys; print(sys.executable)" | The exact interpreter in use, which settles most ModuleNotFoundError tickets instantly |
python3 -m venv .venv | Creates an isolated environment for one project |
source .venv/bin/activate | Points your shell and pip at that environment |
python3 -m pip install pkg==1.2.3 | Installs pinned into the interpreter you named, not some other one |
pip list / pip freeze | Installed packages and exact versions for reproduction |
pip check | Conflicts between installed distributions’ declared requirements |
pip show pkg | Version, install location, and dependencies of one package |
uv venv / uv pip sync | Fast environment creation and exact installs from a lock file |
python3 -m py_compile script.py | Syntax check without executing anything |
python3 -X importtime script.py | Per-module import cost, for slow startup hunts |
python3 -m cProfile -s cumtime script.py | Function-level time, sorted by cumulative cost |
python3 -m timeit "expr" | Microbenchmark for one expression across many runs |
py-spy top --pid PID | Live sampling profile of a running process, no restart needed |
py-spy dump --pid PID | Current stack of every thread, the hang diagnosis opener |
python3 -X faulthandler script.py | Stack dumps on crashes and on demand for stuck processes |
python3 -X tracemalloc=25 script.py | Allocation tracking with frames, for memory growth hunts |
/usr/bin/time -v python3 script.py | Peak RSS and wall time, the memory blowup confirmation |
PYTHONUNBUFFERED=1 python3 script.py | Live logs instead of block-buffered silence in pipelines |
PYTHONDONTWRITEBYTECODE=1 | Skips .pyc writes on read-only or awkward filesystems |
pytest -x -q | Fast failure: stop at the first failing test |
pytest -k name --lf | Select by keyword and rerun last failures while iterating |
python3 -m json.tool data.json | Validates and pretty-prints JSON in one move |
The official language reference for every behavior here lives at docs.python.org, and the packaging guides at packaging.python.org cover environments, pyproject, and lock files in full depth.
Key takeaways and FAQ
The through-line of every tier above: Python interview questions reward people who can pair a behavior with its cause. Behavior alone reads as memorization; cause alone reads as theory; together they read as someone who has been paged for their own scripts. Keep that pairing in every answer you rehearse, and this bank becomes a script for the entire hiring loop.
How long should I prepare for Python interview questions?
With daily Python use, a focused week: two days speaking the beginner tier aloud, two on the intermediate tier, one on scenarios, the weekend on the labs. Without regular use, plan three to four weeks and type every snippet yourself, because hands recall what eyes skim. Either way, rehearse out loud; Python interview questions are answered in air, not in your head.
Are Python interview questions different for DevOps and backend roles?
The beginner and intermediate tiers are nearly identical; the scenario emphasis moves. DevOps loops lean toward subprocess, environments, packaging, and glue robustness, while backend loops push toward data modeling, frameworks, and API design. Prepare the full bank either way, because panels mix roles and you cannot predict which half your interviewer owns.
Which Python topics come up most often in interviews?
Mutability and aliasing, the GIL and its concurrency consequences, generators, decorators, exception handling, and the environment story: venvs, pip, and locks. If you can narrate those six fluently and diagnose the six scenario classics in order, you can handle the large majority of Python interview questions in a real loop.
Do Python interview questions include live coding?
Increasingly yes, usually a small debugging exercise rather than an algorithm grill: a broken script, a failing test, or a slow function to profile. The scenario labs on this page exist for exactly that format, and practicing spoken narration while typing is the single best rehearsal for live Python interview questions of that kind.
Which Python version should you know for interviews in 2026?
Modern 3.x, fluently: f-strings, dataclasses, pathlib, and awareness of what shipped in 3.11 and 3.12. The version number matters less than showing you track changes deliberately; interviewers treat “whatever is on the box” as a red flag, and version-aware answers are a quiet differentiator in senior Python interview questions.
What should I read after this page?
Continue with the Linux interview questions bank, because every Python script you ship runs on a Linux box and inherits its process model, then the Go interview questions bank, since Go increasingly competes with Python for tooling work and comparing the two is a common senior-round topic. Then browse the DevOps Prep hub for the rest of the series as it ships.