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.