Skip to content
Tech Interview Prep home
Technical interview guide

Core Data Structures

Lists, tuples, dicts, and sets — their underlying implementations and when each is the right choice.

Read
48 min
Practice MCQs
25
Interview QA
25
Edition
v2
Editorial status
Reviewed

Scope: Python 3.14; notes distinguish language guarantees from CPython implementation behavior.

Overview

Curated: · Written: · Reviewed:

Python 3.14 core data structures

Interview-grade Python starts with object identity and contracts, not syntax trivia. A container holds references to objects; assignment normally creates another reference, and mutability determines which changes aliases can observe. The right answer therefore connects list, tuple, dictionary, and set behavior to ownership, hash/equality invariants, algorithmic cost, and the exact workload.

list mutability and aliasing

A list is a mutable sequence, so two names can refer to one list and observe each other's in-place changes.

Assignment binds another name; it does not copy. Methods such as append, extend, sort, and reverse mutate the existing object and conventionally return None.

Interview trap. Treating b = a as a snapshot causes distant code to change the same container unexpectedly.

Engineering practice. Make ownership explicit; use a.copy(), slicing, or a comprehension for a shallow copy, and document intentional sharing.

list growth and insertion cost

Appending to a list is amortized constant time, while inserting or deleting near the front shifts later references and is linear time.

CPython over-allocates list storage so most appends reuse capacity; an occasional resize copies references, distributing that cost over many appends.

Interview trap. Calling every append strictly O(1) ignores resize events, while calling it O(n) ignores amortized analysis.

Engineering practice. Use a list for stack-like end operations and collections.deque for frequent operations at both ends.

list sorting semantics

list.sort mutates one list and returns None; sorted accepts any iterable and returns a new list.

Python sorting is stable, so records equal under the key preserve their prior relative order and can be sorted in deliberate passes.

Interview trap. Assigning result = values.sort() replaces the useful name with None rather than the sorted list.

Engineering practice. Use a key function instead of a comparison callback, and rely on stability only when the prior order is intentional and tested.

tuple immutability

A tuple's sequence of references cannot be replaced, but an object referenced by the tuple may itself remain mutable.

Immutability applies to tuple slots: a tuple containing a list cannot replace that list reference, yet the contained list can still append.

Interview trap. Describing a tuple as deeply immutable leads to unsafe assumptions about nested state and hashability.

Engineering practice. Use tuples for fixed-shape records only when field meaning is clear; use named records or dataclasses when names improve the contract.

singleton tuple syntax

The comma creates a tuple; parentheses usually only group an expression.

Therefore (value,) and value, are one-element tuples, while (value) is simply value.

Interview trap. Writing (item) when an API expects a one-element tuple silently passes the item itself.

Engineering practice. Include parentheses and the trailing comma for readable singleton tuples, especially in calls and configuration.

packing and unpacking

Iterable unpacking assigns elements by position, and one starred target can collect the remaining elements into a list.

The non-starred arity must match; extended unpacking consumes enough values for required targets and gathers the middle remainder.

Interview trap. Assuming unpacking validates a semantic record shape beyond element count can accept values in the wrong meaning or order.

Engineering practice. Use unpacking for small, explicit shapes and validate externally sourced records before binding business names.

dictionary key lookup

Dictionary lookup uses a key's hash to locate candidates and equality to identify the matching key.

Equal hashable values must have equal hashes; collisions are legal and resolved by equality checks rather than treated as identity.

Interview trap. Believing hashes are unique identifiers creates incorrect caches, persistence formats, and security assumptions.

Engineering practice. Implement eq and hash together for value objects, and never persist hash() as a stable cross-process identifier.

hashability

A hashable object has a hash value that remains stable during its lifetime and an equality relation consistent with that hash.

Immutable built-ins are often hashable, but a tuple is hashable only when all its elements are hashable; lists and dictionaries are not.

Interview trap. Equating immutable syntax with hashability misses nested mutable members and user-defined equality behavior.

Engineering practice. Choose immutable value keys, test equality/hash invariants, and use frozen value objects only when their fields also satisfy the contract.

mutable dictionary keys

Lists cannot be dictionary keys because mutation could change equality or logical value after placement.

A dictionary depends on a key continuing to map to the same hash bucket; changing hash-relevant state would make the entry unreachable or inconsistent.

Interview trap. Converting a list to a tuple is insufficient when that tuple still contains unhashable members.

Engineering practice. Normalize compound keys into recursively hashable values and avoid user objects whose hash depends on mutable fields.

dictionary insertion order

Python dictionaries preserve insertion order as a language guarantee, but equality between ordinary dictionaries does not compare that order.

Updating an existing key retains its position; deleting and reinserting it places it at the end. Iteration and views expose the maintained order.

Interview trap. Assuming ordered iteration means dictionaries are sequence-equivalent or that equal dictionaries must have identical iteration histories.

Engineering practice. Use order when it is part of presentation or deterministic processing, but represent order-sensitive equality with an explicitly suitable type.

dictionary views

keys(), values(), and items() return dynamic view objects rather than detached lists.

A view reflects later dictionary mutations; keys and items views also support set-like operations when their elements meet hashability requirements.

Interview trap. Keeping a view as an immutable snapshot produces results that change when the source dictionary changes.

Engineering practice. Materialize list(mapping.items()) when a snapshot is required, and avoid changing dictionary size while iterating its live view.

missing dictionary keys

Subscription raises KeyError for a missing key, while get returns a default without inserting it.

setdefault returns an existing value or inserts the supplied default; defaultdict invokes its factory through missing on subscription.

Interview trap. Using get when absence must be distinguished from a stored None value collapses two different states.

Engineering practice. Choose explicit subscription, membership, get, setdefault, or defaultdict according to whether absence is exceptional and whether lookup may mutate.

dictionary merging

When mappings are merged, later values replace earlier values for duplicate keys while key position follows documented insertion behavior.

update mutates a dictionary; the | operator creates a new dictionary and |= updates in place, with right-hand values winning conflicts.

Interview trap. Treating merge as conflict detection silently discards one value when duplicate keys should instead be rejected.

Engineering practice. Validate disjointness when collisions are errors, and make precedence visible when layering defaults, files, environment, and command-line settings.

set uniqueness

A set stores one representative of each equality class of hashable members and does not provide positional indexing.

Membership uses hashing and equality, while iteration order is not an API guarantee suitable for business logic or serialization.

Interview trap. Observing stable iteration in one run and treating it as insertion order creates nondeterministic tests and output.

Engineering practice. Sort set values when deterministic presentation is required, and use a dictionary when uniqueness plus insertion order is the actual requirement.

set algebra

Union, intersection, difference, and symmetric difference express membership operations directly and usually more clearly than nested loops.

Method forms accept any iterable in several cases, while operator forms generally require set-like operands; operators also encode precedence that deserves parentheses.

Interview trap. Confusing a - b with symmetric difference loses elements unique to b and answers a different question.

Engineering practice. Name intermediate sets around domain meaning and estimate their memory cost before materializing large populations.

frozenset

frozenset provides immutable set semantics and is hashable when its members are hashable.

That permits a set value to become a dictionary key or member of another set while retaining union and intersection operations.

Interview trap. Calling frozenset deeply immutable overlooks mutable state reachable through unusual user-defined hashable objects.

Engineering practice. Use frozenset for unordered value identity, permissions, and cache keys when order is irrelevant and members are stable.

membership complexity

List membership scans values linearly, while dictionary and set membership are average-case constant time under ordinary hash behavior.

Hash tables trade additional memory for direct bucket access; worst cases and expensive hash or eq implementations still matter.

Interview trap. Repeating x in large_list inside a loop can accidentally create quadratic work even though the syntax looks simple.

Engineering practice. Build a set once for repeated membership tests, but include construction cost and retained memory in the workload-level decision.

sequence slicing

Ordinary list slicing creates a new outer list containing references to the selected objects.

The copy is shallow: replacing an outer slot is independent, but mutating a shared nested object remains visible through both lists.

Interview trap. Using matrix[:] as a deep copy leaves rows aliased and produces surprising cross-copy mutations.

Engineering practice. Model ownership deliberately; copy nested levels explicitly or use copy.deepcopy only after considering identity, cycles, and resources.

repetition aliasing

Sequence repetition repeats references, so [[0] * n] * m creates m references to one inner list.

Mutating one row then appears to mutate every row because all outer slots point at the same object.

Interview trap. The compact multiplication expression looks like allocation of independent rows but does not call the inner expression repeatedly.

Engineering practice. Use a comprehension such as [[0] * n for _ in range(m)] when each nested mutable object must be independent.

mutable default arguments

Default argument expressions are evaluated once when the function definition executes, not on every call.

A default list or dictionary is therefore shared by all calls that omit that argument, which can intentionally cache state but usually leaks callers together.

Interview trap. Expecting def add(x, items=[]) to allocate a fresh list per call creates persistent, order-dependent behavior.

Engineering practice. Use None as a sentinel and allocate inside the function, while preserving a caller-supplied empty container by testing is None rather than truthiness.

truth testing containers

Empty built-in containers are false and non-empty containers are true, but truth testing cannot always distinguish absence from an intentionally empty value.

if not value combines None, [], {}, set(), empty text, and numeric zero into one branch.

Interview trap. Replacing an explicit is None check with a truthiness check can overwrite valid caller input or defaults.

Engineering practice. Use truthiness for genuine emptiness questions and identity checks for sentinel-state questions.

container comparison

Lists and tuples compare lexicographically by corresponding elements, while sets use subset relationships rather than lexicographic ordering.

Cross-type numeric elements may compare, but unrelated element types can raise TypeError when ordering reaches them.

Interview trap. Sorting heterogeneous records without a normalization key relies on comparisons Python does not define.

Engineering practice. Provide a key that maps values into a deliberate common ordering and avoid using set ordering operators as a total order.

deque versus list

collections.deque supports approximately constant-time appends and pops at both ends, unlike a list's linear front insertion and deletion.

A deque is optimized for endpoint access and rotation, while indexed access near the middle is not its strength.

Interview trap. Replacing every list with deque because queues are fast can degrade code that needs dense indexing, slicing, or sorting.

Engineering practice. Match the container to dominant operations and communicate whether the abstraction is a stack, queue, sequence, or random-access table.

Counter semantics

Counter is a dictionary subclass for tallying hashable objects and can retain zero or negative counts.

Missing elements read as zero; mathematical operations have documented handling of non-positive results that differs from ordinary dictionary merging.

Interview trap. Assuming a zero count removes the key makes length, equality, and iteration conclusions unreliable.

Engineering practice. Delete entries when absence is semantically required and use Counter operations only after checking their treatment of signed counts.

choosing container contracts

Container choice should encode required semantics—ordering, uniqueness, key lookup, mutability, and dominant operations—before micro-optimizing.

Lists, tuples, dictionaries, sets, frozensets, and specialized collections expose different guarantees that downstream code comes to rely on.

Interview trap. Selecting by surface convenience and changing later can silently alter equality, iteration order, aliasing, and complexity.

Engineering practice. State the invariant and workload, choose the narrowest fitting abstraction, then profile representative data rather than reciting complexity alone.

Worked example: assignment aliases; copy does not

a = [1] then b = a then b.append(2) mutates the one list both names refer to. c = a.copy() then c.append(3) leaves a and b at [1, 2].

stepabcid(a)==id(b)
a=[1]; b=a[1][1]—true
b.append(2)[1, 2][1, 2]—true
c=a.copy(); c.append(3)[1, 2][1, 2][1, 2, 3]true

backup = records is the same table. A shallow copy still shares nested objects; copy.deepcopy is the next interview follow-up, not the default.