Python Interview Idioms
Interviewers read a lot of code. Clear, idiomatic Python signals that you know the language and lets you focus on the algorithm instead of syntax. This page collects the idioms that come up again and again. None of them change what your code does, but they make it shorter and easier to read, which reduces bugs under pressure.
If you are new to the primer track, start with Python for DSA first, then come back here.
Comprehensions build collections in one line#
A comprehension replaces a build-up loop with a single readable expression. Reach for it whenever you are creating a list, set, or dict from another iterable.
1nums = [1, 2, 3, 4, 5, 6]
2
3# List: squares of the even numbers
4even_squares = [n * n for n in nums if n % 2 == 0]
5print(even_squares) # [4, 16, 36]
6
7# Set: unique remainders
8remainders = {n % 3 for n in nums}
9print(remainders) # {0, 1, 2}
10
11# Dict: number -> its square
12square_map = {n: n * n for n in nums}
13print(square_map) # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36}
Keep comprehensions to one level of logic. A comprehension with two conditions and a nested loop is usually harder to read than a plain loop, so do not force it.
Generator expressions avoid building the whole list#
A generator expression looks like a comprehension with parentheses. It produces items one at a time instead of building a full list in memory, which is ideal when you only need to feed the values into a function like sum, any, all, min, or max.
1nums = [3, 1, 4, 1, 5, 9, 2, 6]
2
3# No intermediate list is created here
4total = sum(n * n for n in nums)
5print(total) # 173
6
7# Short-circuits as soon as it finds a match
8has_big = any(n > 8 for n in nums)
9print(has_big) # True
When a generator expression is the only argument to a function, you can drop the extra parentheses: sum(n for n in nums).
enumerate: index and value together#
When you need the position and the item, use enumerate instead of indexing by hand. This shows up constantly in array problems.
1letters = ["a", "b", "c"]
2
3for i, letter in enumerate(letters):
4 print(i, letter) # 0 a, 1 b, 2 c
5
6# Start counting from a different number
7for i, letter in enumerate(letters, start=1):
8 print(i, letter) # 1 a, 2 b, 3 c
Avoid for i in range(len(letters)) followed by letters[i]. It is longer and easier to get wrong.
zip: walk two sequences in parallel#
zip pairs up items from multiple iterables. It stops at the shortest one, so unequal lengths do not raise an error.
1names = ["ana", "ben", "cid"]
2scores = [90, 85, 70]
3
4for name, score in zip(names, scores):
5 print(name, score) # ana 90, etc.
6
7# Build a dict directly from two lists
8lookup = dict(zip(names, scores))
9print(lookup) # {'ana': 90, 'ben': 85, 'cid': 70}
You can also “unzip” with the * operator: pairs = list(zip(names, scores)) then back_names, back_scores = zip(*pairs).
Slicing: subsequences without loops#
Slicing uses sequence[start:stop:step]. start is inclusive, stop is exclusive, and negative indices count from the end.
1data = [0, 1, 2, 3, 4, 5]
2
3print(data[1:4]) # [1, 2, 3]
4print(data[:3]) # [0, 1, 2]
5print(data[3:]) # [3, 4, 5]
6print(data[-2:]) # [4, 5] (last two)
7print(data[::2]) # [0, 2, 4] (every other)
8print(data[::-1]) # [5, 4, 3, 2, 1, 0] (reversed)
Slicing a list copies that slice, so data[:] is a quick shallow copy. Slicing a string works the same way and returns a new string. Remember that data[::-1] is convenient but creates a full reversed copy, which is O(n) space.
Tuple unpacking and swapping#
Python lets you assign several names at once. This makes swaps and multiple returns clean and readable.
1# Swap without a temp variable
2a, b = 1, 2
3a, b = b, a
4print(a, b) # 2 1
5
6# Unpack a returned pair
7def min_max(values):
8 return min(values), max(values)
9
10low, high = min_max([4, 9, 1, 7])
11print(low, high) # 1 9
12
13# Grab the first item and the rest with a star
14first, *rest = [10, 20, 30, 40]
15print(first, rest) # 10 [20, 30, 40]
The classic two-pointer swap nums[i], nums[j] = nums[j], nums[i] relies on this and evaluates the right side fully before assigning.
Truthiness: empty means False#
Empty containers, empty strings, 0, and None are all falsy. You rarely need to write len(x) == 0 or x == None.
1items = []
2if not items:
3 print("nothing to process") # runs
4
5name = ""
6print(bool(name)) # False
7
8# Use "is" for None, not ==
9value = None
10if value is None:
11 print("no value yet") # runs
Be careful: 0 is falsy, so if count: skips a real count of zero. When zero is a valid value, test if count is not None: instead.
The ternary expression#
Python writes a one-line conditional as value_if_true if condition else value_if_false.
1n = 7
2parity = "even" if n % 2 == 0 else "odd"
3print(parity) # odd
4
5# Common in comprehensions to transform each item
6labels = ["fizz" if x % 3 == 0 else str(x) for x in range(1, 6)]
7print(labels) # ['1', '2', 'fizz', '4', '5']
The walrus operator (:=)#
The walrus operator assigns a value and returns it in the same expression. It shines when you would otherwise compute something twice or repeat a call inside a loop.
1# Reuse a computed length without calling len twice
2data = [1, 2, 3, 4, 5, 6, 7, 8]
3if (n := len(data)) > 5:
4 print(f"list has {n} items") # list has 8 items
5
6# Read chunks until a sentinel (pattern, not runnable here)
7# while (line := next_line()) != "STOP":
8# process(line)
Use it sparingly. It is a nice tool for avoiding duplicate work, but overusing it makes code harder to follow.
Where to go next#
These idioms pay off across the whole 60-day curriculum. Once they feel natural, review the Big-O cheat sheet so you can reason about the cost of the operations behind them, then keep practicing on the algorithms themselves.