← all topics

1 · Python fluency for interviews Code

Collections, idioms, built-ins and what each operation costs · the base for every coding question

Why it matters for NPE. The Meta guide grades syntax/language familiarity, structure, bugs and efficiency. In a 45-minute CoderPad round you cannot afford to look up how Counter or sorted(key=) works.

Primer: what "language fluency" means to the grader

The guide says the interviewer scores efficiency, structure, syntax/language familiarity, bugs and correctness. In practice that means: you pick the right container the moment you hear the problem, you write it without pausing to remember an argument order, and you can say the cost of every line. This page is the toolbox. Everything later builds on it.

Rule for the interview: if you forget a method name, say "I'll leave a placeholder for the sort key and come back", write the intent, and move on. The guide explicitly allows it. Freezing on syntax is the only real failure here.

Watch

Python for Coding Interviews: Everything you need to KnowNeetCode · 26:18

Watch all of it once. Then rewatch 8:40 arrays, 12:38 sorting, 15:25 strings, 16:50 queues, 17:30 hash sets, 18:25 hash maps and 20:55 heaps until you can type each snippet from memory.

Lists, Tuples, and SetsCorey Schafer · 29:04

Only if list/tuple/set still feel interchangeable. Slower and more thorough than NeetCode.

Dictionaries: Working with Key-Value PairsCorey Schafer · 9:59

get, in, items(), deleting keys. Ten minutes, worth it if you've mostly used JavaScript objects.

Sorting Lists, Tuples, and ObjectsCorey Schafer · 12:07

sorted(key=…), reverse=True, sorting by several fields with a tuple key. You will use this in half the problems.

Static Arrays, Dynamic Arrays, and Strings - Big O Complexity - DSA Course in Python Lecture 2Greg Hogg · 18:23

The cost side: why append is amortised O(1), insert(0) is O(n), and string concatenation in a loop is quadratic (11:15 strings, 13:30 Python code).

The containers and what each operation costs

ContainerUse it whenO(1)Not O(1)
listOrdered sequence, index access, stack (append/pop())a[i], append, pop() (end), lenx in a O(n), insert(0,x) and pop(0) O(n), sort O(n log n)
tupleImmutable record; hashable, so usable as a dict key or set membersame as list for readscannot change; build a new one
setMembership, dedup, intersection/differenceadd, remove/discard, x in s (expected)no order, no indexing; a & b O(min(len))
dictKey → value; counting, grouping, caching, lookup tablesd[k], d.get(k, default), k in d, assignment, del (expected)iteration O(n); keys must be hashable (no lists)
collections.dequeQueue (BFS), sliding window of recent itemsappend, appendleft, popleft, popindexing in the middle O(n)
heapq on a listRepeatedly need the smallest (or largest, negate) item; top-kheap[0] peekheappush/heappop O(log n), heapify O(n)
CounterFrequency map in one line; most_common(k)same as dictmost_common(k) O(n log k)
defaultdict(list|int|set)Group-by without if key not in dsame as dictreading a missing key creates it (trap below)

Dict and set costs are expected O(1) because of hashing. If asked "worst case?", say O(n) with pathological collisions, then move on. Python 3.7+ dicts keep insertion order; sets do not.

Idioms you must type without thinking

# iterate with index, or two sequences in lockstep
for i, host in enumerate(hosts):            # i starts at 0; enumerate(hosts, 1) starts at 1
    ...
for host, status in zip(hosts, statuses):   # stops at the shorter one
    ...

# counting and grouping
from collections import Counter, defaultdict, deque
counts = Counter(words)                     # counts['x'] is 0 for missing keys, no KeyError
counts.most_common(3)                       # [(word, n), ...] highest first
groups = defaultdict(list)
for rec in records:
    groups[rec.region].append(rec)
seen = {}
seen[key] = seen.get(key, 0) + 1            # the manual version of Counter

# sorting
sorted(items, key=lambda x: (-x[1], x[0]))  # count descending, then name ascending
items.sort(key=len)                         # in place, returns None (trap!)
sorted(d.items(), key=lambda kv: kv[1])     # dict by value

# strings
parts = line.strip().split(',')             # strip() first or the last field keeps '\n'
line.split()                                # no argument: any whitespace, drops empties
','.join(fields)                            # fields must all be str
s.lower(), s.isdigit(), s.isalnum(), s.startswith('web-')
f"{host}: {count} failures"

# comprehensions and unpacking
squares = [x * x for x in nums if x % 2 == 0]
lookup = {h.lower(): s for h, s in pairs}
first, *rest = values
a, b = b, a

# truthiness and defaults
if not items:          # empty list/dict/set/str and 0 and None are all falsy
    ...
value = d.get(k) or 'unknown'   # careful: 0 and '' are also "or"-replaced

Mutability: where bugs come from

Mutable (change in place)Immutable (every "change" is a new object)
list, dict, set, deque, your classesint, float, str, tuple, frozenset, bool, None

Reading and writing files (preview of topic 5)

with open(path, encoding='utf-8') as f:    # closes the file even on exceptions
    for line in f:                          # streams one line at a time: O(1) memory per line
        line = line.rstrip('\n')
        if not line:
            continue

Never f.read() or f.readlines() a file you were told might be large. The streaming loop is the answer to "what if the file is 20 GB?"

Big-O in one breath

State the cost in terms of the inputs the interviewer named: n lines, u distinct hosts, k results. "O(n) time to scan the file, O(u) space for the counts, plus O(u log k) to pick the top k with a heap." Common shapes:

ShapeTypical code
O(1)dict/set lookup, list index, append/pop at the end
O(log n)binary search, one heap push/pop
O(n)one pass over the input, Counter(), building a set
O(n log n)sorting the whole input
O(n log k)keeping a size-k heap while scanning n items
O(n²)nested loops over the same input, x in list inside a loop, repeated insert(0)

Nested loops are not automatically O(n²): a loop over rows and then over that row's cells is O(total cells).

Interview questions

1. Why is a dict lookup O(1)? When isn't it?Keys are hashed to a slot in an array, so finding a key costs the hash plus a probe or two. With many colliding keys the probe chain grows, so the worst case is O(n). In practice say "expected O(1)".
2. list vs tuple: when would you choose a tuple?When the record should not change, or when it has to be a dict key or set member (lists are unhashable). Also for returning several values from a function.
3. What does lst.sort() return? And sorted(lst)?lst.sort() sorts in place and returns None. sorted(lst) returns a new sorted list and works on any iterable (dict keys, sets, generators).
4. Sort hosts by failure count descending, then by name ascending.sorted(counts.items(), key=lambda kv: (-kv[1], kv[0])). Negate the number for descending; the string sorts ascending by default. Python's sort is stable, so equal keys keep their input order.
5. Why is pop(0) on a list a problem for a queue?Every element shifts left, so it is O(n). Use collections.deque with popleft(), which is O(1).
6. What is wrong with def add(item, bucket=[])?The default list is created once at definition time and shared by every call, so items leak between calls. Use None as the default and create the list inside.
7. How do you count words in a list in one line? And get the top 3?Counter(words) and Counter(words).most_common(3). Mention that most_common(k) uses a heap internally, O(n log k).
8. What is the difference between d[k], d.get(k) and defaultdict?d[k] raises KeyError if missing. d.get(k, default) returns the default without inserting. defaultdict(int)[k] inserts and returns the default, which is what you want when counting but can pollute the dict if you use it for a read-only check.
9. Why avoid building a string with += in a loop?Strings are immutable, so each += can copy the whole string: O(n²) total. Append pieces to a list and ''.join(parts) once, O(n).
10. How do you read a 20 GB log file in Python?Open it with with open(...) and iterate for line in f. The file object is a lazy iterator, so only one line is in memory at a time. Keep aggregate state small (a dict keyed by the thing you count), and stream the output too if it could be large.

Traps

Do before marking this topic done

In a blank editor, no autocomplete, write these from memory and run them on tiny inputs:

  1. Read lines of host,status from a string split on newlines, count DOWN per host, print hosts sorted by count descending then name.
  2. Return the first element that appears exactly once in a list, in O(n).
  3. Given a list of (start, end) tuples, return them sorted by start, and say what the sort costs.
  4. Implement a FIFO queue with deque and a stack with a list, pushing 3 and popping 3 from each.
  5. Return the k largest numbers from a list with heapq.nlargest, then without it using a size-k min-heap.

Then do the three warm-up problems below. They are trivial by design: the goal is zero syntax hesitation.

Practice: linked problems

Logged attempts feed the tracker. Open the problem in a new tab, solve in a blank editor, then log honestly.

all topics · 2 · How a packet gets from A to B →