Skip to content

PUBLIC QUESTION PREVIEW

Python interview questions

Decorators, generators, GIL, async, Pythonic patterns, memory model.

Showing 12 of 100 source questions. Sample answers are study material, not employer-specific hiring guidance.

How to work through this preview

Attempt each question before reading its answer. For a concept, give a definition and a concrete example. For code, state constraints, walk through a small input, and check complexity. For design, separate requirements from assumptions and explain failure handling. Use real situations for behavioral answers.

QUESTION 1 / medium / coding

What is a decorator in Python? Write a simple timing decorator.

Sample answer

A decorator is a function that takes a function and returns a modified version of it. import time, functools def timer(func): @functools.wraps(func) def wrapper(*args, **kwargs): start = time.time() result = func(*args, **kwargs) print(f"{func.__name__} took {time.time() - start:.4f}s") return result return wrapper @timer def slow_fn(): time.sleep(1) slow_fn() # prints: slow_fn took 1.0001s functools.wraps preserves __name__, __doc__ — important for debugging and introspection.

Hints to guide your next attempt

  • A decorator is a function that wraps another function
  • Use functools.wraps to preserve the original function's metadata

Source tags: decorator / functools / python / higher-order-function

QUESTION 2 / medium / conceptual

What is the GIL in Python? How does it affect multithreading?

Sample answer

The GIL is a mutex in CPython that allows only one thread to execute Python bytecode at a time, even on multi-core machines. Impact: CPU-bound tasks (number crunching) don't benefit from threading in Python — threads take turns on one core. For I/O-bound tasks (network requests, file I/O), threading still helps because threads release the GIL while waiting. Solutions for CPU-bound parallelism: - multiprocessing module — spawns separate processes, each with its own GIL - C extensions that release the GIL (numpy, pandas do this) - PyPy (has GIL too but much faster execution) - Python 3.13+ is experimenting with per-interpreter GIL (nogil fork)

Hints to guide your next attempt

  • GIL = Global Interpreter Lock
  • Only one thread executes Python bytecode at a time

Source tags: gil / threading / cpython / concurrency / python

QUESTION 3 / medium / conceptual

What is a generator in Python? How does yield work? When would you use one over a list?

Sample answer

A generator is a function that uses yield instead of return. Each call to next() resumes from where it left off. def count_up(n): i = 0 while i < n: yield i i += 1 gen = count_up(1_000_000) next(gen) # 0 — only one value computed vs list(range(1_000_000)) — allocates all 1M integers at once. Use generators when: - Working with large/infinite sequences (log files, streams) - You only need values one at a time - Memory efficiency matters Generators are lazy — values are computed only when requested.

Hints to guide your next attempt

  • yield pauses function execution and returns a value
  • Generators are lazy — they produce values on demand

Source tags: generator / yield / lazy-evaluation / memory / python

QUESTION 4 / easy / conceptual

What is the difference between deepcopy and shallow copy in Python?

Sample answer

Shallow copy (copy.copy or list slicing): creates a new container but nested objects are still references to the same objects. a = [[1, 2], [3, 4]] b = a.copy() # shallow b[0].append(99) # modifies a[0] too! Both point to the same inner list. Deep copy (copy.deepcopy): recursively copies all nested objects. Completely independent. b = copy.deepcopy(a) b[0].append(99) # a is unchanged Use deepcopy when you need full independence. Shallow copy is faster and sufficient for flat structures.

Hints to guide your next attempt

  • Shallow copy copies the container but not nested objects
  • deepcopy recurses into all nested objects

Source tags: copy / deepcopy / reference / python

QUESTION 5 / easy / conceptual

What are *args and **kwargs in Python? Give an example.

Sample answer

*args: collects extra positional arguments into a tuple. **kwargs: collects extra keyword arguments into a dictionary. def greet(*args, **kwargs): for name in args: print(f"Hello {name}") for key, val in kwargs.items(): print(f"{key}: {val}") greet("Alice", "Bob", city="Mumbai", role="Engineer") # Hello Alice, Hello Bob, city: Mumbai, role: Engineer Useful for: wrapper functions, decorators, flexible APIs, passing through arguments.

Hints to guide your next attempt

  • *args collects positional arguments as a tuple
  • **kwargs collects keyword arguments as a dict

Source tags: args / kwargs / functions / python

QUESTION 6 / easy / conceptual

What is the difference between a list and a tuple in Python?

Sample answer

List: mutable, defined with [], allows append/remove/modify. Tuple: immutable, defined with (), faster access, hashable (can be dict key or set element). a = [1, 2, 3] # list b = (1, 2, 3) # tuple Use tuples for: fixed records (coordinates, RGB), dict keys, return values signaling immutability. Use lists for: collections you need to grow or modify. Memory: tuples use less memory — CPython pre-allocates extra slots for list growth. Tuples of constants are interned at compile time; lists never are.

Hints to guide your next attempt

  • Mutability is the key difference
  • Tuples can be used as dict keys

Source tags: list / tuple / mutability / python / basics

QUESTION 7 / easy / conceptual

What are mutable and immutable types in Python? Give examples and explain the implications.

Sample answer

Immutable: int, float, str, bool, tuple, frozenset, bytes — cannot be changed after creation. Mutable: list, dict, set, bytearray, most user-defined classes. Why it matters: - Only immutable (hashable) objects can be dict keys or set members. - Mutable defaults in functions are a common bug — shared across all calls. - Reassigning an immutable variable creates a NEW object, not modifying the old one. a = "hello" b = a a += " world" print(b) # "hello" — b still points to the original string object For heavy string concatenation, use "".join(parts) — avoids creating O(n) intermediate strings.

Hints to guide your next attempt

  • Immutable: int, str, tuple, frozenset
  • Mutable: list, dict, set

Source tags: mutable / immutable / python / types / basics

QUESTION 8 / easy / conceptual

What is the difference between "is" and "==" in Python?

Sample answer

== checks value equality (calls __eq__). is checks identity — whether two names point to the exact same object (same id()). a = [1, 2, 3] b = [1, 2, 3] a == b # True — same values a is b # False — different objects Interning gotcha: CPython interns small integers (-5 to 256) and short strings. x = 256; y = 256; x is y # True (interned) x = 257; y = 257; x is y # False (not interned, implementation detail) Rule: use == for value comparisons always. Only use "is" for: None checks (if x is None:), True/False, or deliberate identity checks.

Hints to guide your next attempt

  • "is" checks identity (same object in memory)
  • "==" checks equality (calls __eq__)

Source tags: is / equality / identity / python / gotcha

QUESTION 9 / easy / conceptual

What are comprehensions in Python? Show list, dict, set, and generator forms.

Sample answer

Comprehensions are concise, readable syntax to build collections. List: [x * 2 for x in range(10) if x % 2 == 0] Dict: {k: v for k, v in pairs} Set: {x**2 for x in nums} Generator: (x**2 for x in nums) # lazy, no full allocation vs map+filter (less readable): list(map(lambda x: x*2, filter(lambda x: x%2==0, range(10)))) Nested comprehension: matrix = [[1,2,3],[4,5,6]] flat = [x for row in matrix for x in row] # [1,2,3,4,5,6] Performance: comprehensions are slightly faster than equivalent loops (optimized bytecode). Generator expressions: use when you only iterate once and memory matters.

Hints to guide your next attempt

  • [expr for item in iterable if condition]
  • Generator comprehension uses () — lazy evaluation

Source tags: comprehension / generator-expression / python / basics

QUESTION 10 / easy / conceptual

What are lambda functions? When should and should not you use them?

Sample answer

Lambda: anonymous function limited to a single expression. square = lambda x: x ** 2 sorted(items, key=lambda x: x['score']) When to use: - Sort/filter keys: sorted(data, key=lambda x: x.age) - Quick callbacks: button.command = lambda: handle() - One-liners where a named function adds no clarity When NOT to use: - Complex logic — use def for readability and debuggability - Assigning to a variable — PEP 8 prefers def for named functions - Anything needing docstrings, type hints, or multiple statements Lambdas appear as "<lambda>" in tracebacks — harder to debug. Use functools.partial as an alternative to capture arguments without a lambda.

Hints to guide your next attempt

  • lambda args: expression — single expression only
  • Good for sort keys, bad for complex logic

Source tags: lambda / anonymous-function / python / functional

QUESTION 11 / medium / conceptual

Explain Python's LEGB scoping rule with an example of global and nonlocal.

Sample answer

LEGB: the order Python looks up names. L — Local: inside current function E — Enclosing: outer function(s) for nested functions G — Global: module level B — Built-in: len, print, etc. x = "global" def outer(): x = "enclosing" def inner(): x = "local" print(x) # "local" inner() global: lets a function assign to a global variable. nonlocal: lets a nested function assign to its enclosing (not global) variable. def counter(): count = 0 def inc(): nonlocal count count += 1 return count return inc c = counter() c() # 1 c() # 2

Hints to guide your next attempt

  • LEGB = Local, Enclosing, Global, Built-in
  • nonlocal lets nested functions write to enclosing scope

Source tags: legb / scope / global / nonlocal / python

QUESTION 12 / medium / conceptual

What is the mutable default argument bug in Python? How do you fix it?

Sample answer

Bug: mutable default arguments (list, dict, set) are shared across all calls to the function. def append_to(item, lst=[]): # lst is ONE object created at def time lst.append(item) return lst append_to(1) # [1] append_to(2) # [1, 2] ← not [2]! Shared across calls. Fix: use None as sentinel, create fresh container inside. def append_to(item, lst=None): if lst is None: lst = [] lst.append(item) return lst This applies to any mutable default: [], {}, set(). Immutable defaults (0, "", None, True, (1,2)) are always safe. Bonus: dataclass fields with mutable defaults require field(default_factory=list).

Hints to guide your next attempt

  • Default arguments are evaluated ONCE at function definition time
  • Fix: use None as sentinel

Source tags: mutable-default / gotcha / python / bugs / functions

Check understanding, not recognition

  • Can you explain the answer without reading it?
  • Can you show when the approach fails or needs a different assumption?
  • Can you connect the concept to a real project, task, or decision you have made?
  • Can you distinguish a simplified interview example from a production requirement?

A LITTLE MORE CLARITY

Frequently asked questions

How many Python questions are shown here?

This public preview shows 12 questions with the supplied answers from the app's 100-question Python bank. Full interactive practice requires signing in.

Should I memorize the sample answers?

Use them as reference explanations, not a script or a guarantee of technical correctness for every environment. Explain your assumptions, validate code against your language and version, and discuss trade-offs. Behavioral answers should be built from your own experience.

What do hints and solution steps mean?

Hints identify ideas to consider before reading the answer. When the source question includes steps, they are shown as a separate walkthrough. Example capacities and design targets in a solution are assumptions for that exercise, not real system statistics.

BUILD YOUR NEXT OPPORTUNITY

Practice Python actively.

Sign in to use the full topic bank and interactive practice workspace.

Open Topic Practice