InterviewGrind Get notified

InterviewGrind › Python

Python Interview Questions and Answers

Practice the Python questions interviews actually ask: real bugs to spot, code to predict, and the follow-ups that test whether you understand why. Each answer is explained, and every "what does this output" answer was checked by running the code.

15 free questions from the app. Try each one before you open the answer.

1. Joining stringsEasy

Building the ID list for a log line crashes with "expected str instance, int found". What fixes it?

line = ", ".join(order_ids)
  1. ", ".join(order_ids) → ", ".join(map(str, order_ids))
  2. ", ".join(order_ids) → str(", ").join(order_ids)
  3. ", ".join(order_ids) → ", ".join(order_ids, str)
  4. ", ".join(order_ids) → ", " + str(order_ids)
Show answer

Answer: A. ", ".join(order_ids) → ", ".join(map(str, order_ids))

str.join only accepts strings. Convert as you go with map(str, ...) or a generator: ", ".join(str(i) for i in order_ids).

2. Lazy sequencesEasy

primes() is an infinite generator. Which line gets the first 5 values without hanging?

  1. list(itertools.islice(primes(), 5))
  2. list(primes())[:5]
  3. primes()[:5]
  4. sorted(primes())[:5]
Show answer

Answer: A. list(itertools.islice(primes(), 5))

islice pulls items lazily and stops after 5, so it works on generators, files and any other iterator, infinite or not.

3. Logging levelsEasy

logger.info(...) messages never appear, but logger.warning(...) ones do. Why?

  1. Logging defaults to WARNING; configure level=logging.INFO
  2. info messages only appear when the app runs in debug mode
  3. Messages from info go to a separate log file by default
  4. logger.info needs a format string with %s placeholders
Show answer

Answer: A. Logging defaults to WARNING; configure level=logging.INFO

The root logger’s level is WARNING until you configure it. Call logging.basicConfig(level=logging.INFO) once at startup (or configure handlers properly in larger apps).

4. IterationEasy

Pairing user IDs with names, one user silently goes missing. What does this print?

print(list(zip([1, 2, 3], "ab")))

What does it output?

  1. [(1, 'a'), (2, 'b')]
  2. [(1, 'a'), (2, 'b'), (3, None)]
  3. [(1, 'ab'), (2, 'ab'), (3, 'ab')]
  4. ValueError
Show answer

Answer: A. [(1, 'a'), (2, 'b')]

zip stops at the shortest input, silently dropping the 3. Use itertools.zip_longest to pad, or zip(..., strict=True) (3.10+) to raise on a length mismatch.

5. Context managersEasy

Which review comment matters most on this report export?

f = open(path, "w")
f.write(render(report))
f.close()
  1. If render raises, the file stays open; use with open(...)
  2. Open with mode "a" so an earlier export is never overwritten
  3. Call f.flush() before f.close(), or the final write can be lost
  4. Write the file in binary mode, which makes the export faster
Show answer

Answer: A. If render raises, the file stays open; use with open(...)

with open(path, "w") as f: closes the file even when render throws. Leaked handles add up in long-running services, and on Windows an open file can’t be replaced or deleted.

6. ConcurrencyEasy

Myth or fact: even with the GIL, threads can make a Python program that waits on many network calls much faster.

  1. Myth
  2. Fact
Show answer

Answer: B. Fact

Fact. Threads release the GIL while waiting on I/O, so 50 network calls can be in flight at once and finish far sooner. What threads can't speed up is CPU-bound Python code; use processes or native libraries for that.

7. TruthinessEasy

Customers explicitly given a 0% discount get the 10% default instead.

  1. def apply_discount(price, discount=None):
  2. if not discount:
  3. discount = DEFAULT_DISCOUNT
  4. return price * (1 - discount)

Which line has the bug?

Show answer

Answer: Line 2: if not discount:

not discount is true for 0 as well as None, because 0 is falsy. Test for the missing value itself: if discount is None:.

8. CollectionsEasy

Counting words crashes with KeyError on the very first word. What fixes it?

counts = {}
for word in words:
    counts[word] += 1
  1. counts = {} → counts = defaultdict(int)
  2. counts[word] += 1 → counts[word] = 1
  3. counts = {} → counts = set()
  4. for word in words → for word in set(words)
Show answer

Answer: A. counts = {} → counts = defaultdict(int)

defaultdict(int) creates 0 for a missing key, so += 1 works. collections.Counter(words) does the whole job in one call, and counts.get(word, 0) + 1 works with a plain dict.

9. In-place methodsEasy

top_names crashes with "'NoneType' object is not subscriptable".

  1. def top_names(users):
  2. names = [u.name for u in users]
  3. names = names.sort()
  4. return names[:10]

Which line has the bug?

Show answer

Answer: Line 3: names = names.sort()

list.sort() sorts in place and returns None, so names becomes None. Call names.sort() on its own line, or use names = sorted(names), which returns a new list.

10. FlatteningEasy

Flatten [1, [2, [3, 4]], 5] into [1, 2, 3, 4, 5]. Which approach handles any depth?

  1. Recurse into every item that is itself a list
  2. list(itertools.chain(*nested))
  3. [x for sub in nested for x in sub]
  4. sum(nested, [])
Show answer

Answer: A. Recurse into every item that is itself a list

For each item: if it’s a list, flatten it and extend the result; otherwise append it. Every nesting level is just another call.

11. Top-k frequentEasy

Return the k most frequent words in a list. What’s the simplest correct approach?

  1. Counter(words).most_common(k)
  2. Sort the words, then take the first k of them
  3. sorted(set(words), key=words.count)[-k:]
  4. max(words, key=words.count), called k times
Show answer

Answer: A. Counter(words).most_common(k)

Counter counts in one O(n) pass, and most_common(k) uses a heap internally, so the whole thing is O(n log k).

12. SetsEasy

You have sets of users active last week and this week. Which expression gives users who were active last week but not this week?

  1. last_week - this_week
  2. this_week - last_week
  3. last_week & this_week
  4. last_week ^ this_week
Show answer

Answer: A. last_week - this_week

Set difference keeps items in the left set that aren’t in the right, in O(len(left)) time. It’s the churn list in one line.

13. DeduplicationEasy

Remove duplicate emails from a list, keeping the first occurrence of each, in order. What’s the cleanest way?

  1. list(dict.fromkeys(emails))
  2. list(set(emails))
  3. sorted(set(emails))
  4. [e for e in emails if emails.count(e) == 1]
Show answer

Answer: A. list(dict.fromkeys(emails))

Dicts keep insertion order (Python 3.7+), and fromkeys ignores repeats, so you get each email once in first-seen order in O(n).

14. SerializationEasy

Sending the webhook fails with "Object of type datetime is not JSON serializable".

  1. def payload(order):
  2. body = {
  3. "id": order.id,
  4. "total": str(order.total),
  5. "created": order.created_at,
  6. }
  7. return json.dumps(body)

Which line has the bug?

Show answer

Answer: Line 5: "created": order.created_at,

The json module only encodes basic types. Convert the datetime yourself, ideally to ISO 8601 in UTC: order.created_at.isoformat(). A default= function on json.dumps works too, but explicit conversion keeps the format obvious.

15. SlicingEasy

Orders are sorted oldest first, and the recent-orders widget shows the three oldest. What fixes it?

recent = orders[:3]
  1. orders[:3] → orders[-3:]
  2. orders[:3] → orders[3:]
  3. orders[:3] → orders[:-3]
  4. orders[:3] → orders[::3]
Show answer

Answer: A. orders[:3] → orders[-3:]

Negative indexes count from the end, so [-3:] is the last three items. Add [::-1] if the newest should come first.

Practise like this every day

InterviewGrind gives you ten questions like these each day on your iPhone, with Pro tracks for company-style loops.

Get the launch email