Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Python Module 5

Built-in data structures: lists, dicts, sets and more interview questions

Interview questions on “Built-in data structures: lists, dicts, sets and more”: warm-ups, then what each harder question tests and how a good answer starts.

33 questions: 11 warm-ups and 22 with model answers · Levels: Fresher to senior

Warm-up questions

Short questions with short answers, each answered in full.

  1. Fresher

    What is the difference between append() and extend() on a Python list?

    Answered in the lesson Lists: create, index, slice and modify

  2. Fresher to mid

    How does a shallow copy of a nested list differ from a deep copy?

    Answered in the lesson References, aliasing, shallow and deep copies

  3. Fresher

    What is the difference between sorted() and list.sort()?

    Answered in the lesson Sorting with key functions and stability

  4. Fresher to mid

    Why can a tuple be a dictionary key when a list cannot?

    Answered in the lesson Tuples, unpacking and named tuples

  5. Fresher

    What happens when you look up a missing key with d[key], d.get(key) and d.setdefault(key, [])?

    Answered in the lesson Dictionaries: keys, values and views

  6. Fresher to mid

    Why is x in a_set usually faster than x in a_list, and when is it not?

    Answered in the lesson Sets and frozensets for membership and maths

  7. Fresher

    What is the difference between [x for x in xs if x > 0] and [x if x > 0 else 0 for x in xs]?

    Answered in the lesson List, dict and set comprehensions

  8. Fresher

    On a defaultdict(list), what is the difference between d[key] and d.get(key) when the key is missing?

    Answered in the lesson collections: deque, Counter, defaultdict and more

  9. Fresher

    How does heapq represent a heap, and where is its smallest item?

    Answered in the lesson heapq, bisect and array for efficient data

  10. Fresher

    For the string "नमस्ते", what is the difference between len(s) and len(s.encode("utf-8"))?

    Answered in the lesson Text vs bytes: Unicode, encodings, bytearray

  11. Fresher

    Which Python structures would you use for a stack, a queue and a priority queue, and why?

    Answered in the lesson Choosing the right data structure

Questions with model answers

What each question tests, the first step of a good answer and the follow-ups to expect.

  1. Fresher to mid

    A support desk keeps waiting tickets in a Python list, adds new ones with append() and serves the oldest with pop(0). With a few hundred tickets nobody notices; with 200,000 waiting, the service spends most of its CPU time inside pop(0). Explain why, estimate the work, and fix it.

    What it tests
    Knowing that removing from the front of a list moves every other item, estimating the total work, and choosing a structure built for queues.
    First step
    Say what pop(0) does: a CPython list is an array of references, so removing the first one moves every remaining reference one place to the left. Serving n tickets that way moves about n × n ÷ 2 references, some 2 × 10¹⁰ for 200,000 tickets, while a queue that removes from the front in constant time does 200,000 small steps.
    Follow-ups

    From the lesson Lists: create, index, slice and modify

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page.

    Opening the model answers with your pass…

    Model answers open with any MySmartCoPilot pass.

    • Pro pass · 1 week₹79$2.99
    • 30 days₹179$5.99
    • 1 year₹1,699$45

    One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

    It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  2. Mid to senior

    A script appends 10 million floats to a list, one at a time. Why is each append fast on average, although the list has to grow many times, and why does the finished list need about 320 MB when 10 million 8-byte numbers are only 80 MB? How would you cut the memory?

    What it tests
    Dynamic-array growth and amortised constant-time append, what a list really stores, and when to store numbers in an array instead.
    First step
    Split the memory in two: the list holds one 8-byte reference per item plus a few spare slots, about 80 MB, and every float is a separate 24-byte object, about 240 MB more. Then explain growth: when the array is full, CPython allocates a bigger one with room to spare, so the copying happens only now and then.
    Follow-ups

    From the lesson Lists: create, index, slice and modify

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  3. Fresher to mid

    A web service keeps DEFAULT_PREFS = {"theme": "light", "recent": []} at module level and starts each new user with prefs = DEFAULT_PREFS.copy(). After a few hours, new users see items that other people viewed. Explain the bug and fix it.

    What it tests
    What a shallow copy shares, mutable state at module level, and a fix that keeps working when the defaults gain more nesting.
    First step
    Name what copy() copies: a new outer dictionary whose values are the same objects as before. Every user's prefs["recent"] is therefore one shared list, and an append through any user lands in it. Show it in one line: prefs_a["recent"] is prefs_b["recent"] returns True.
    Follow-ups

    From the lesson References, aliasing, shallow and deep copies

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  4. Mid to senior

    A board game keeps an undo history by calling copy.deepcopy(board) before every move, where the board is a list of 100 lists of 100 cells. A profile shows most of the time inside deepcopy. Why is it slow, and what would you do instead?

    What it tests
    What deepcopy does for each object it meets, measuring copying costs, and choosing an undo design that copies less or nothing.
    First step
    Count the work: the board is 101 lists and 10,100 references, and deepcopy visits every one in Python code, with a memo entry for each list, before every move. Then ask what a move changes: usually one or two cells. Copying the rows with row[:] is far cheaper, and recording only the change is cheaper still.
    Follow-ups

    From the lesson References, aliasing, shallow and deep copies

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  5. Mid to senior

    Log records are (timestamp, level, message) tuples whose timestamps are fixed-width ISO 8601 strings. Sort a million of them so that CRITICAL comes first, then ERROR, WARNING and INFO, and within each level the newest first. Write the key, and say what the sort costs.

    What it tests
    Turning a custom category order into numbers, mixing directions when a field cannot be negated, and measuring key-based sorts.
    First step
    Map each level to a rank with a dictionary, CRITICAL 0 to INFO 3. A string timestamp cannot be negated, so either sort in two passes, newest first and then by rank, or make one descending sort whose key is (-rank, timestamp): descending by -rank means ascending by rank, and timestamps come out newest first.
    Follow-ups

    From the lesson Sorting with key functions and stability

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  6. Mid to senior

    An app lists 50,000 customer names from many countries with sorted(names). Users report that every name starting with a small letter comes after all the capitalised ones, and that Émile appears after Zoya. Explain why, and design a sort that people accept. What does your fix cost?

    What it tests
    Code-point order against human collation, casefold() and Unicode normalisation, locale-aware sort keys, and the cost of computing keys.
    First step
    Explain the default: strings compare by code point, so the capitals A to Z (65 to 90) sort before the small letters (97 to 122), and accented letters, from 192 upwards, after both. key=str.casefold fixes case; the order people expect needs a collation key for their language, computed once per name.
    Follow-ups

    From the lesson Sorting with key functions and stability

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  7. Mid to senior

    After t = (1, [2]), the statement t[1] += [3] raises TypeError, yet t is (1, [2, 3]) afterwards. Explain step by step what Python did, whether this is a bug, and how you would design a record that is truly immutable.

    What it tests
    Augmented assignment as separate steps, an immutable container holding mutable contents, and designing records that cannot change.
    First step
    Split the statement the way Python runs it: read t[1], apply += to that object, then store the result back into t[1]. For a list, += extends the list in place and succeeds; storing into a tuple's item is not allowed, so the last step raises after the list has already changed.
    Follow-ups

    From the lesson Tuples, unpacking and named tuples

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  8. Mid to senior

    A service passes user records around as plain tuples (id, name, email), unpacked or indexed in about 40 places. Product needs a phone field between name and email. What breaks, and would you move to a NamedTuple, a dataclass or a dict? Compare their safety and memory.

    What it tests
    The fragility of positional records, choosing a record type, migrating call sites safely and the memory each record type needs.
    First step
    List what breaks: every unpacking into three names raises ValueError, which is loud, while every index such as record[2] silently reads the phone instead of the email, which is worse. Then propose named fields, with the new field added at the end or given a default, so positional code keeps working during the migration.
    Follow-ups

    From the lesson Tuples, unpacking and named tuples

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  9. Mid to senior

    A Point class defines __eq__ and __hash__ from its x and y attributes, which can be changed. Points are keys in a dictionary of visited cells. After one point's x is changed, visited[p] raises KeyError and p in visited is False, although the entry is still in the dictionary. Explain, and fix the class.

    What it tests
    How a hash table finds a key, the rule that a key's hash must not change, and making value objects safe to use as keys.
    First step
    Describe the lookup: the dictionary uses hash(key) to decide where to look and then compares candidates with ==. The entry sits where the old hash pointed; after x changes, the new hash sends every lookup somewhere else, so the stored key is never even compared. The class broke the rule that a key's hash never changes.
    Follow-ups

    From the lesson Dictionaries: keys, values and views

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  10. Mid to senior

    Settings come from defaults, a config file and environment variables, merged with {defaults, file_cfg, **env_cfg}. When the file sets only database.host, the default database.port disappears. Explain why, describe the merge you would write, and compare |, update() and collections.ChainMap for this job.

    What it tests
    Shallow merge semantics, writing a recursive merge and its edge cases, and when a ChainMap view beats a merged copy.
    First step
    Show the cause: unpacking merges top-level keys only, so the whole database dictionary from the file replaces the default one, port included. The fix walks both dictionaries together: where both sides hold a dictionary under the same key, merge those two recursively; anywhere else, the later value wins.
    Follow-ups

    From the lesson Dictionaries: keys, values and views

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  11. Mid to senior

    A file of 5 million lines holds one user ID per line, many of them repeated. Write each ID out once, in the order first seen, and report how many lines were repeats. Which structure do you use, how much memory does it need, and what changes if each line is a JSON record instead?

    What it tests
    Streaming de-duplication with a set, estimating memory per item from measurements, and making unhashable records hashable.
    First step
    Stream the file and keep a set of the IDs seen so far: for each line, if the ID is not in the set, write it and add it, otherwise count a repeat. Writing as you go keeps the first-seen order without storing any order, and each line costs one average constant-time lookup.
    Follow-ups

    From the lesson Sets and frozensets for membership and maths

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  12. Fresher to mid

    A unit test checks that str(active_users()) equals "{'asha', 'ravi', 'mei'}", where active_users() returns a set. It passes on a developer's laptop and fails in CI now and then. Explain the cause, why a similar test on a set of small integers never failed, and fix the test.

    What it tests
    Hash randomisation, what decides the order of a set, and testing results that have no order.
    First step
    Point at what the test compares: text. A set has no defined order, and the order it prints follows the hashes of its items. String hashes are salted with a random value in each new Python process, so the printed order can change from run to run, and only one order matches the expected text.
    Follow-ups

    From the lesson Sets and frozensets for membership and maths

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 3 minutes
  13. Fresher to mid

    A report computes total = sum([len(line) for line in log_file]) over a log of 50 million lines. On a small server it runs out of memory before printing anything. Explain why, fix it, and say when you would still build the list.

    What it tests
    The difference between a list comprehension and a generator expression in memory, what a list of integers really costs, and when building a list is the right call.
    First step
    The brackets build the whole list of 50 million lengths before sum() sees the first one: about 420 MB of references on a 64-bit build, plus a 28-byte int object for every length above 256. Drop the brackets: sum(len(line) for line in log_file) hands sum() one length at a time, so memory stays flat whatever the size of the file.
    Follow-ups

    From the lesson List, dict and set comprehensions

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  14. Mid to senior

    This works at module level but raises NameError in a class body: class Grid: size = 8; cells = [(r, c) for r in range(size) for c in range(size)]. Which size fails, and why? Give two fixes, and say whether a generator expression or a lambda in the class body would behave differently.

    What it tests
    Class scopes and comprehension scopes, which part of a comprehension runs in the enclosing scope, and applying the same rule to generator expressions and lambdas.
    First step
    Split the comprehension into its parts. Only the iterable of the first for clause, the first range(size), is evaluated in the class body. The second range(size) runs inside the comprehension's own scope, and names defined in a class body are invisible to nested scopes, so that lookup goes on to the globals and fails.
    Follow-ups

    From the lesson List, dict and set comprehensions

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 5 minutes
  15. Mid to senior

    A monitor counts API calls in calls = defaultdict(int). A dashboard runs is_heavy(user), returning calls[user] > 1000, for all 3 million users in the directory, most of whom never call the API. Memory jumps by over 100 MB and len(calls), the active-user metric, becomes 3 million. Explain, fix it, and say how a test catches it.

    What it tests
    Knowing that a defaultdict read with square brackets inserts, the memory an entry costs, and testing side effects as well as return values.
    First step
    Reading calls[user] for a missing user runs the factory: int() returns 0, and the defaultdict stores that 0 under the user before returning it. Every check of a silent user therefore adds an entry. Check with calls.get(user, 0) > 1000 instead, or make calls a Counter, whose reads of missing keys return 0 without storing anything.
    Follow-ups

    From the lesson collections: deque, Counter, defaultdict and more

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  16. Fresher to mid

    A service receives 50,000 sensor readings a second and, after each one, reports the average of the last 1,000. Compare a list with append() and pop(0), a deque(maxlen=1000) with sum() each time, and a deque with a running total. What does each cost per reading, and which would you ship?

    What it tests
    Counting the work per operation, seeing which cost dominates, and keeping an O(1) aggregate over a sliding window.
    First step
    Count the work per reading. The list version moves 999 references on every pop(0) and then adds 1,000 values. The bounded deque drops the oldest reading in constant time, but sum() still adds 1,000 values. A running total adds the new reading and subtracts the one that leaves, which is constant work however long the window is.
    Follow-ups

    From the lesson collections: deque, Counter, defaultdict and more

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  17. Fresher to mid

    A job scheduler pushes (priority, job) tuples onto a heapq heap. Tests pass, but production crashes with TypeError: '<' not supported between instances of 'Job' and 'Job'. Explain when it happens, fix it so that jobs of equal priority run in submission order, and let callers cancel a job that is already queued.

    What it tests
    How tuples compare item by item, a stable priority queue with a unique tie-breaker, and removing entries from a heap without breaking it.
    First step
    Tuples compare item by item, so whenever two priorities are equal, heapq compares the jobs themselves, and Job defines no ordering. The tests probably never had two pending jobs with the same priority meet in a comparison. Push (priority, next(sequence), job) with sequence = itertools.count(): the number is unique, so comparisons stop before the jobs, and ties keep submission order.
    Follow-ups

    From the lesson heapq, bisect and array for efficient data

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  18. Mid to senior

    A file holds 50 million transaction amounts, one per line. Report the 10 largest using as little memory as you can. Compare sorted(), heapq.nlargest(10, ...) and a min-heap of size 10 kept by hand, in time and memory, and say when you would choose each.

    What it tests
    Streaming with bounded memory, the size-k min-heap pattern, and comparing O(n log n) sorting with O(n log k) selection with real numbers.
    First step
    sorted() must hold all 50 million values, about 2 GB as Python floats and references, and does O(n log n) work. A min-heap of the 10 largest seen so far keeps its smallest at heap[0]: each new amount is compared with heap[0] and replaces it with heapreplace() only when larger. That is O(n log 10) time and O(10) memory, and heapq.nlargest(10, stream) does exactly this.
    Follow-ups

    From the lesson heapq, bisect and array for efficient data

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  19. Mid to senior

    A script reads export.csv with open("export.csv", newline="") and csv.DictReader. Exports from your server work. A file a colleague saved from a spreadsheet program fails with KeyError: 'name', and on the colleague's Windows laptop the script also prints José as José. Explain both symptoms and fix the script.

    What it tests
    Recognising a UTF-8 byte order mark and mojibake from the locale encoding, reading text files portably, and proving an encoding from the bytes.
    First step
    Treat them as two decoding mistakes. The spreadsheet program wrote a byte order mark, ef bb bf, before the header, and decoding as plain UTF-8 keeps it as an invisible U+FEFF stuck to "name". On Windows, open() without encoding= uses the locale's code page, often cp1252, which turns the two UTF-8 bytes of é into é. Open the file with encoding="utf-8-sig".
    Follow-ups

    From the lesson Text vs bytes: Unicode, encodings, bytearray

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  20. Mid to senior

    Users sign up with names typed on many devices. Your duplicate check treats two sign-ups that both display as Zoë as different people, and two Devanagari names that look identical are also stored twice. Explain why, and design what you store, what you compare, and what you use for unique login names.

    What it tests
    Composed and decomposed forms, NFC against NFKC, caseless matching, and separating stored text, comparison keys and identifiers.
    First step
    Compare code points, not pixels: one Zoë has ë as the single code point U+00EB, the other an e followed by the combining diaeresis U+0308, so == sees different strings; the Devanagari pair differs the same way around a nukta. Normalise to NFC when text enters the system, store that, and compare NFC forms, adding casefold() where case must not matter.
    Follow-ups

    From the lesson Text vs bytes: Unicode, encodings, bytearray

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  21. Mid to senior

    A game server keeps 2 million (score, player) pairs in a list and calls sorted() on every request to show the top 10 and the requesting player's rank. Scores change thousands of times a second. Redesign the in-memory structures and give the cost of each operation.

    What it tests
    Splitting operations by frequency, combining a dict with an ordered structure, and putting numbers on sort, insort and rank queries.
    First step
    List the operations with their rates: score updates, thousands a second; the top 10 and one player's rank, every page. Sorting 2 million pairs per request costs seconds of CPU for ten names and one number. Keep a dict from player to score for updates, and a separate structure for order, such as counts per score in a Fenwick tree, so that updates and ranks cost O(log n).
    Follow-ups

    From the lesson Choosing the right data structure

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes
  22. Mid to senior

    Design an in-process rate limiter: each user may make at most 100 requests in any 60-second window. Which structures hold the state, what does one request cost, and how do you stop memory from growing with users who have stopped calling?

    What it tests
    Choosing a dict of deques for sliding windows, amortised costs, memory per user, approximate alternatives and cleaning up idle state.
    First step
    Keep a dict from user to a deque of the timestamps of their recent allowed requests. On each request, pop timestamps older than 60 seconds from the left, then allow the request if fewer than 100 remain and append its timestamp. Every timestamp is appended once and popped once, so a request costs amortised O(1).
    Follow-ups

    From the lesson Choosing the right data structure

    Model answer, Pro.

    In the answer:

    • Model answer
    • Follow-up answers
    • Common mistakes
    • Rubric
    • Variants
    • about 4 minutes

All Python interview questions · The Python track

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.