HireHireInterview Quizzes › Python Developer

Python Developer Interview Questions

Think you're ready? These are the questions that actually decide Python Developer interviews. Warm up on Easy — then face the Hard round, where 95% of candidates crumble. 30 questions across 3 levels, instant score, completely free.

30Questions
3Difficulty levels
95%Fail the hard round
FreeInstant score
Easy
Warm-up · 10 Qs
Medium
Practical · 10 Qs
Hard
Brutal · 10 Qs
⚡ Take the Python Developer quiz — get your score →

The Python Developer interview questions

Below are the real questions, grouped by difficulty. Expand any one to reveal the correct answer and why — or take the timed quiz for a score you can share. Can you clear the Hard round?

Easy round 10 questions

What does the following print? x = [1, 2, 3] y = x y.append(4) print(len(x))
  • A. 3
  • B. 4 ✓
  • C. Raises an error
  • D. None
Correct answer: B. y = x binds both names to the same list object, so appending via y also affects x, making its length 4.
Which statement about Python's Global Interpreter Lock (GIL) in CPython is correct?
  • A. It allows multiple threads to execute Python bytecode truly in parallel on multiple cores
  • B. It ensures only one thread executes Python bytecode at a time within a process ✓
  • C. It applies only to multiprocessing, not threading
  • D. It prevents deadlocks in all multithreaded programs
Correct answer: B. The GIL ensures that only one thread executes Python bytecode at a time in a single CPython process, limiting CPU-bound thread parallelism.
What is the output? def f(a, b=[]): b.append(a) return b print(f(1)) print(f(2))
  • A. [1] then [2]
  • B. [1] then [1, 2] ✓
  • C. [1, 2] then [1, 2]
  • D. Raises an error
Correct answer: B. The default list is created once and shared across calls, so it accumulates: [1] then [1, 2].
Which data structure gives O(1) average time for membership testing (the `in` operator)?
  • A. list
  • B. tuple
  • C. set ✓
  • D. collections.deque
Correct answer: C. A set is backed by a hash table, giving average O(1) membership tests, whereas list/tuple/deque require O(n) linear scans.
In a Django or Flask ORM-backed app, what problem does the 'N+1 query' issue describe?
  • A. Running one query per related object in a loop instead of fetching them together ✓
  • B. Opening N+1 simultaneous database connections
  • C. A query that returns one extra unwanted row
  • D. An off-by-one error in LIMIT/OFFSET pagination
Correct answer: A. The N+1 problem is issuing one initial query plus one additional query per row for related data, fixable with eager loading like select_related/joinedload.
What is the correct way to safely open and automatically close a file for reading in Python?
  • A. f = open('data.txt'); f.read()
  • B. with open('data.txt') as f: data = f.read() ✓
  • C. f = File('data.txt', 'r')
  • D. open('data.txt').close().read()
Correct answer: B. The with statement uses a context manager that guarantees the file is closed even if an exception occurs during reading.
What does the `@staticmethod` decorator indicate about a method?
  • A. It receives the instance as the first argument automatically
  • B. It receives the class as the first argument automatically
  • C. It receives neither the instance nor the class implicitly ✓
  • D. It can only be called on class instances, never the class
Correct answer: C. A staticmethod takes no implicit self or cls argument; it behaves like a plain function namespaced inside the class.
What is the result of `2 ** 3 ** 2` in Python?
  • A. 64
  • B. 512 ✓
  • C. 36
  • D. 12
Correct answer: B. The ** operator is right-associative, so it evaluates as 2 ** (3 ** 2) = 2 ** 9 = 512.
Which HTTP status code should a REST API return when a request succeeds and a new resource is created?
  • A. 200 OK
  • B. 201 Created ✓
  • C. 204 No Content
  • D. 302 Found
Correct answer: B. 201 Created is the standard status indicating a request succeeded and resulted in a new resource being created.
What is the difference between `is` and `==` in Python?
  • A. `is` compares values; `==` compares identity
  • B. `is` compares identity (same object); `==` compares values (equality) ✓
  • C. They are always interchangeable
  • D. `==` works only on numbers; `is` works only on strings
Correct answer: B. `is` checks whether two references point to the same object in memory, while `==` checks whether their values are equal.

Medium round 10 questions

You have a list of dictionaries and want to sort them by the 'age' key in descending order. Which is the correct approach?
  • A. sorted(people, key='age', reverse=True)
  • B. sorted(people, key=lambda p: p['age'], reverse=True) ✓
  • C. people.sort(by=lambda p: p['age'], desc=True)
  • D. sorted(people, lambda p: -p['age'])
Correct answer: B. sorted() takes a callable key function and a reverse boolean, so a lambda extracting p['age'] with reverse=True is correct.
What is the result of the following code? x = [1, 2, 3] y = x y.append(4) print(x)
  • A. [1, 2, 3]
  • B. [1, 2, 3, 4] ✓
  • C. [4, 1, 2, 3]
  • D. Raises an error
Correct answer: B. y = x binds both names to the same list object, so appending through y also affects x, printing [1, 2, 3, 4].
In a Python function, why is using a mutable default argument like def f(items=[]) considered a common bug?
  • A. The default list is created once and shared across all calls, so it accumulates values ✓
  • B. Python forbids lists as default arguments and raises a SyntaxError
  • C. The list is recreated on every call, wasting memory
  • D. It makes the function run slower on each call
Correct answer: A. Default arguments are evaluated once at function definition, so a mutable default persists and accumulates state across calls.
Which statement correctly opens a file for reading and guarantees it is closed afterward, even if an exception occurs?
  • A. f = open('data.txt'); f.read(); f.close()
  • B. with open('data.txt') as f: data = f.read() ✓
  • C. f = open('data.txt', 'r'); data = f.read
  • D. open('data.txt').read().close()
Correct answer: B. The with statement uses the file as a context manager, ensuring the file is closed automatically even if an exception is raised inside the block.
What does the following list comprehension produce? [x*x for x in range(5) if x % 2 == 0]
  • A. [0, 1, 4, 9, 16]
  • B. [0, 4, 16] ✓
  • C. [1, 9]
  • D. [0, 2, 4]
Correct answer: B. It squares only the even numbers 0, 2, and 4 from range(5), yielding [0, 4, 16].
You need to catch a specific exception when converting user input to an integer with int(user_input). Which exception should you catch?
  • A. TypeError
  • B. ValueError ✓
  • C. KeyError
  • D. RuntimeError
Correct answer: B. int() raises ValueError when the string cannot be interpreted as an integer, such as int('abc').
What is the primary purpose of a Python virtual environment (venv)?
  • A. To speed up the execution of Python scripts
  • B. To isolate project-specific dependencies from other projects and the system Python ✓
  • C. To compile Python code into a standalone binary
  • D. To automatically format code according to PEP 8
Correct answer: B. A virtual environment isolates a project's installed packages so different projects can have different, non-conflicting dependency versions.
Given d = {'a': 1, 'b': 2}, which expression safely returns 0 when the key 'c' is missing instead of raising an error?
  • A. d['c']
  • B. d.get('c', 0) ✓
  • C. d.fetch('c', 0)
  • D. d['c'] or 0
Correct answer: B. dict.get('c', 0) returns the default 0 when the key is absent, while d['c'] would raise a KeyError.
What is the difference between the == operator and the is operator in Python?
  • A. == compares object identity; is compares values
  • B. == compares values (equality); is compares object identity (same object in memory) ✓
  • C. They are identical and interchangeable
  • D. is works only on numbers; == works only on strings
Correct answer: B. == checks whether two objects have equal values, while is checks whether they are the exact same object in memory.
In a Python module, what does the guard if __name__ == '__main__': accomplish?
  • A. It ensures the file only runs on the main thread
  • B. It runs the enclosed code only when the file is executed directly, not when imported ✓
  • C. It marks the function that Python calls first automatically
  • D. It prevents the module from being imported by other files
Correct answer: B. __name__ equals '__main__' only when the file is run directly, so the block is skipped when the module is imported elsewhere.

Hard round 10 questions

You run this code: ```python def make_multipliers(): return [lambda x: i * x for i in range(4)] print([f(10) for f in make_multipliers()]) ``` What is printed, and why?
  • A. [0, 10, 20, 30] — each lambda captures its own copy of i at creation time
  • B. [30, 30, 30, 30] — all lambdas share i via late binding and see its final value 3 ✓
  • C. [0, 0, 0, 0] — i is reset to 0 when the list comprehension exits
  • D. TypeError — i is undefined inside the lambda scope
Correct answer: B. Closures capture the variable i by reference, not value, so all four lambdas read i's final value (3) when called, giving 3*10 four times.
A function accumulates data across calls: ```python def add_item(item, bucket=[]): bucket.append(item) return bucket print(add_item(1)) print(add_item(2)) ``` What is printed?
  • A. [1] then [2]
  • B. [1] then [1, 2] ✓
  • C. [1] then [2, 1]
  • D. [1, 2] then [1, 2]
Correct answer: B. The default list is created once at function-definition time and shared across every call that omits the argument, so it accumulates: [1] then [1, 2].
Your CPU-bound number-crunching function is slow, so you split it across 4 threads via threading.Thread on CPython 3.11. Wall-clock time barely improves (and sometimes worsens). What is the correct explanation and fix?
  • A. Threads block on I/O; switch to asyncio to overlap the waits
  • B. The GIL serializes bytecode execution so only one thread runs Python at a time; use multiprocessing to get true parallelism ✓
  • C. Thread creation overhead dominates; reuse a ThreadPoolExecutor and it will parallelize
  • D. The default recursion limit throttles threads; raise sys.setrecursionlimit and threads will scale
Correct answer: B. Under the GIL only one thread executes Python bytecode at a time, so CPU-bound work doesn't parallelize with threads; multiprocessing (separate interpreters/processes) sidesteps the GIL.
Consider: ```python a = 256 b = 256 c = 257 d = 257 print(a is b, c is d) ``` Run as a normal script (not line-by-line in a REPL). What is the most reliable output?
  • A. True True ✓
  • B. True False
  • C. False False
  • D. False True
Correct answer: A. CPython caches small integers from -5 to 256 so a is b is True; and because c and d are compiled in the same code block, the peephole/const-folding often interns 257 too, so both are True in a script.
You have this class hierarchy: ```python class A: def __init__(self): print('A'); super().__init__() class B(A): def __init__(self): print('B'); super().__init__() class C(A): def __init__(self): print('C'); super().__init__() class D(B, C): def __init__(self): print('D'); super().__init__() D() ``` What is printed?
  • A. D B A C A
  • B. D B A
  • C. D B C A ✓
  • D. D B C A C A
Correct answer: C. C3 linearization gives MRO D→B→C→A→object, and cooperative super() walks it exactly once each, so A prints only once after C: D B C A.
A colleague writes a data-only descriptor to validate an attribute but it seems ignored — reads return the instance-dict value directly. Which single fact explains when an instance __dict__ entry shadows a class-level descriptor?
  • A. Instance dict always wins over any class attribute, descriptor or not
  • B. A non-data descriptor (only __get__) is shadowed by an instance-dict entry, but a data descriptor (defines __set__ or __delete__) takes precedence ✓
  • C. Descriptors only work on classes using __slots__
  • D. Data descriptors are shadowed by the instance dict; non-data descriptors always win
Correct answer: B. Attribute lookup gives data descriptors (those defining __set__/__delete__) priority over the instance dict, while non-data descriptors are overridden by an instance-dict entry of the same name.
You must call a blocking, thread-unsafe legacy function `legacy_query()` from inside an async coroutine without freezing the event loop. Which approach is correct and safe?
  • A. await legacy_query() — awaiting any callable offloads it automatically
  • B. result = await asyncio.get_running_loop().run_in_executor(None, legacy_query) ✓
  • C. asyncio.create_task(legacy_query()) so it runs concurrently on the loop
  • D. Wrap it: result = await asyncio.wait_for(legacy_query(), timeout=5)
Correct answer: B. run_in_executor moves the blocking call onto a worker thread and returns an awaitable, so the event loop keeps running; awaiting a plain blocking function or wrapping a non-coroutine in create_task/wait_for is invalid or still blocks.
This Django view triggers a performance alarm: ```python for book in Book.objects.all(): print(book.author.name) # author is a ForeignKey ``` With 500 books, roughly how many DB queries run and what is the right fix?
  • A. 1 query; already optimal
  • B. 501 queries (N+1); fix with Book.objects.select_related('author') ✓
  • C. 501 queries (N+1); fix with Book.objects.prefetch_related('author')
  • D. 2 queries; fix with .only('author')
Correct answer: B. Each book.author triggers a separate query (1 + 500 = 501, the N+1 problem); select_related does a SQL JOIN for the forward ForeignKey in one query, whereas prefetch_related is for reverse/many-to-many relations.
Predict the output: ```python def gen(): try: yield 1 yield 2 finally: print('cleanup') g = gen() print(next(g)) g.close() print('done') ```
  • A. 1, then done (finally never runs because we didn't exhaust it)
  • B. 1, then cleanup, then done ✓
  • C. 1, then RuntimeError from close()
  • D. 1, then done, then cleanup
Correct answer: B. generator.close() raises GeneratorExit at the paused yield, which unwinds through the finally block (printing 'cleanup') before close() returns, then 'done' prints.
You need a shallow-vs-deep-copy-safe 3x3 grid initialized to zeros: ```python grid = [[0] * 3] * 3 grid[0][0] = 5 print(grid) ``` What is printed?
  • A. [[5, 0, 0], [0, 0, 0], [0, 0, 0]]
  • B. [[5, 0, 0], [5, 0, 0], [5, 0, 0]] ✓
  • C. [[5, 5, 5], [0, 0, 0], [0, 0, 0]]
  • D. [[5, 5, 5], [5, 5, 5], [5, 5, 5]]
Correct answer: B. The outer `* 3` replicates the same inner-list reference three times, so mutating grid[0][0] is visible through all three rows.

Prep for another role

Questions are original, written and independently verified for HireHire's role interview quizzes. They reflect the kind of knowledge Python Developer interviews test, not any specific company's questions. HireHire maps live tech & IT jobs across India, updated regularly. Last updated: July 2026.