Complete Python Progressive Master Question Bank
75 progressive Python interview questions covering foundations, data structures, object-oriented concepts, memory mechanics, decorators, concurrency, and advanced language internals.
Q1: What is Python and how does its execution flow differ from compiled languages?
Python is a high-level, dynamically typed, interpreted programming language. Source code (.py) is first compiled into intermediate bytecode (.pyc). The Python Virtual Machine (PVM) then interprets this bytecode line-by-line into machine code at runtime.
import dis
def add(a, b):
return a + b
dis.dis(add) # Disassembles bytecode instructions- Compilation to Bytecode (
.pyc) occurs automatically prior to PVM interpretation. - Dynamic typing resolves variable types at runtime rather than compile time.
What component in Python directly executes bytecode (.pyc) files?
Q2: What are the key operational differences between Lists and Tuples?
Lists are mutable sequence structures ([]), allocating extra memory over-allocation for dynamic resizing. Tuples are immutable sequence structures (()), occupying lower fixed memory footprint, faster allocation speeds, and supporting usage as dictionary keys.
- Lists = Mutable, higher memory overhead.
- Tuples = Immutable, lower memory, hashable (can be dict keys).
Can a tuple containing a list element be used as a key inside a Python Dictionary?
Q3: How do Dictionaries and Sets achieve O(1) average lookup times?
Python dictionaries and sets are backed by Hash Tables. Key hashes are computed via hash(key). In Python 3.6+, dictionaries use a compact array layout (an index array + a dense entries array), preserving insertion order while maintaining $O(1)$ key lookup and deletion.
- Uses hash tables for $O(1)$ average lookups.
- Python 3.6+ dicts preserve insertion order while reducing memory by ~20-25%.
What is the average time complexity for searching an element in a Python Set?
Q4: Why are mutable default arguments (e.g. def fn(arg=[])) considered dangerous?
Default argument values are evaluated once when the function definition is executed at module import time, NOT on each function invocation. Passing mutable default arguments ([] or {}) causes all subsequent calls to mutate the exact same shared object instance.
# ❌ DANGEROUS: Shared list reference across invocations
def append_to(element, target=[]):
target.append(element)
return target
# ✅ SAFE PATTERN: Re-instantiate mutable inside call
def safe_append(element, target=None):
if target is None:
target = []
target.append(element)
return target- Default parameter objects bind at function definition time.
- Use
Noneas default placeholder and assign new mutables inside the body.
What is returned by append_to(1) followed by append_to(2) using the dangerous pattern above?
Q5: What is the difference between the 'is' operator and the '==' operator?
==Operator: Checks value equality (invokes__eq__()).isOperator: Checks object identity (verifies if two variables reference the exact same memory address usingid(a) == id(b)).
==tests equivalence of values.istests memory address identity.
What does [1, 2] == [1, 2] and [1, 2] is [1, 2] evaluate to?
Q6: What is the difference between copy.copy() and copy.deepcopy()?
- Shallow Copy (
copy.copy()): Creates a new container object, but populates it with references to child objects inside the original. - Deep Copy (
copy.deepcopy()): Recursively duplicates the container AND all nested child objects, constructing completely independent object graphs.
- Shallow copy duplicates top-level container only.
- Deep copy duplicates container and all nested child object references recursively.
If you modify a nested list inside a shallow copied object, does the original object's nested list update?
Q7: How do *args and **kwargs function in Python signatures?
*args: Captures variable positional parameters as a Tuple.**kwargs: Captures variable keyword parameters as a Dictionary. Allow passing arbitrary parameter lists to functions or forwarding arguments in wrapper functions.
def logger(func):
def wrapper(*args, **kwargs):
print(f"Calling {func.__name__} with {args} and {kwargs}")
return func(*args, **kwargs) # Unpacks parameters seamlessly
return wrapper*argscollects positional arguments into a tuple.**kwargscollects keyword arguments into a dictionary.
What type of data structure is *args stored as inside function scope?
Q8: What are List Comprehensions versus Generator Expressions?
- List Comprehension (
[x for x in data]): Evaluates the entire sequence eagerly, building a full list in heap memory. - Generator Expression (
(x for x in data)): Evaluates elements lazily on-demand, returning an iterator with minimal $O(1)$ memory footprint.
- List Comprehension = Eager evaluation ($O(N)$ memory). Generator Expression = Lazy iteration ($O(1)$ memory).
Which construct should be preferred when streaming 10 GB of log data line-by-line?
Q9: What are Lambda functions and what limitations do they enforce?
Lambda functions are small, anonymous single-line functions created via lambda params: expression. They automatically return expression results and are restricted to a single expression (no statements, assignments, or multiline loops allowed).
- Single-expression inline functions.
- Useful for short callbacks in
map(),filter(), orsorted(key=...).
Can a lambda function contain explicit return statements or assignment operations in Python?
Q10: What is the LEGB Rule for Python scope resolution?
Python resolves variable names in strict sequential order:
- L (Local): Inside active function/lambda body.
- E (Enclosing / Nonlocal): In enclosing nested function scopes.
- G (Global): At module top-level.
- B (Built-in): Preloaded Python built-in names (
len,range).
- Scope resolution order: Local -> Enclosing -> Global -> Built-in.
- Use
globalornonlocalkeywords to rebind variables in outer scopes.
Which keyword must be used inside a nested inner function to rebind a variable declared in its parent outer function scope?