Data Types
Lists, tuples, strings, sets, and dicts — mutability, slicing, comprehensions.
56 questions
JuniorTheoryVery commonWhat is the difference between a list and a tuple in Python?
What is the difference between a list and a tuple in Python?
A list is mutable — you append, insert, or remove; a tuple is immutable once created. They also differ in CPython: list over-allocates so append is amortized O(1), while tuple is fixed-size, more compact, and cached via free lists.
Common mistakes
- ✗Believing list and tuple are identical under the hood — CPython stores and optimizes them differently
- ✗Thinking a tuple is fully immutable even when it holds a mutable element like a list
- ✗Assuming tuples are always faster — the real win is memory and hashability, not blanket speed
Follow-up questions
- →Can a tuple be used as a dict key? What's the catch with nested mutables?
- →Why does
listover-allocate instead of growing by one slot per append?
JuniorTheoryVery commonWhat is a mapping in Python, and what is the canonical example?
What is a mapping in Python, and what is the canonical example?
A mapping gives keyed access to values via methods like get, keys, values, items. The canonical one is dict; the stdlib adds defaultdict, OrderedDict, Counter. Keys must be hashable; values may be anything.
Common mistakes
- ✗Thinking dict values must be hashable — only keys must be
- ✗Believing
dictkeeps keys sorted; since 3.7 it keeps insertion order, not sorted order - ✗Confusing key access by hash with positional access by index
Follow-up questions
- →What's the difference between
d[key]andd.get(key)? - →What does
defaultdictadd over a plaindict?
JuniorTheoryVery commonWhat is a set, and how does frozenset differ from it?
What is a set, and how does frozenset differ from it?
A set is an unordered collection of unique, hashable elements with no indexing or slicing, with fast in checks and the |, &, - operators. set is mutable (add, remove); frozenset is its immutable, hashable twin, usable as a dict key.
Common mistakes
- ✗Expecting a
setto keep insertion order or support indexing: it is unordered, sos[0]and slices raise aTypeError. - ✗Trying to put a
listordictinto a set: elements must be hashable; of the two set types only the immutablefrozensetcan itself be a set element or adictkey.
Follow-up questions
- →Why must set elements be hashable?
- →When would you reach for
frozensetoverset?
JuniorTheoryVery commonHow does slicing s[start:stop:step] work on a sequence?
How does slicing s[start:stop:step] work on a sequence?
It returns a new sub-sequence from start (inclusive) up to stop (exclusive), taking every step-th item. All three are optional; negative indices count from the end and s[:] makes a shallow copy. Out-of-range bounds are clamped, not errors.
Common mistakes
- ✗Expecting
stopto be inclusive - ✗Assuming an out-of-range bound raises instead of being clamped
- ✗Thinking
s[:]is a deep copy rather than a shallow one
Follow-up questions
- →What does a negative step like
s[::-1]produce, and why? - →How does slice assignment
s[1:3] = [...]differ from single-item assignment?
JuniorTheoryVery commonCan you change a single character in a Python str in place?
Can you change a single character in a Python str in place?
No — str is immutable. s[0] = 'x' raises TypeError; every "edit" like replace or + builds a brand-new string object and rebinds the name, leaving the original untouched. Immutability is what makes strings hashable and valid dict keys.
Common mistakes
- ✗Trying
s[i] = cand expecting it to work like a list - ✗Thinking
s.replace(...)mutatessin place instead of returning a new string - ✗Building strings with
+=in a tight loop, unaware each step allocates a new object
Follow-up questions
- →Why does repeated
+=on a string in a loop scale poorly, and what is the fix? - →How does string immutability let CPython safely intern and cache literals?
MiddleTheoryVery commonWhat can and cannot be used as a dict key, and why?
What can and cannot be used as a dict key, and why?
Any hashable object can be a key: int, str, a hashable tuple, even a function or module. list, dict, set cannot — they're mutable and unhashable. A tuple's hash is recursive, so a tuple holding a dict inside is itself unhashable.
Common mistakes
- ✗Thinking every tuple is a valid key — a tuple containing a
list/dictis unhashable - ✗Assuming any collection is barred as a key — a
frozensetis hashable and works fine - ✗Confusing immutability with hashability for the key requirement
Follow-up questions
- →Why is
(1, [2])not a valid dict key? - →How does giving a class an
__eq__affect its usability as a key?
JuniorTheoryCommonWhat does the program print?
What does the program print?
zero
Open full question →Common mistakes
- ✗Expecting
minus, as ifelseended the expression: the part afterelseis itself"minus" if n < 0 else "zero", and0 < 0is false. - ✗Expecting
plus, as if zero counted as positive:0 > 0is false, so the first value is skipped and the check moves on ton < 0.
Follow-up questions
- →How would you write the same choice with
if,elifandelse? - →What does the program print when
n = -3?
JuniorTheoryCommonWhat does the program print?
What does the program print?
0 None False
Open full question →Common mistakes
- ✗Expecting
5for"pear": the default is used only when the key is missing, and"pear"is present with the value0, sogetreturns0. - ✗Expecting a
KeyErrorfor"plum":getnever raises on a missing key; without a default it returnsNone. Onlystock["plum"]raises.
Follow-up questions
- →What would
print(stock["plum"])do instead? - →How would you add one to
stock["pear"]withget?
JuniorTheoryCommonWhat does the program print?
What does the program print?
2 avocado
Open full question →Common mistakes
- ✗Expecting
apple, as if the first key wins: each new pair with the key"a"replaces the old value, so the last such word,avocado, stays. - ✗Expecting
3entries: a dict holds each key once, andappleandavocadoshare the key"a", so only2keys remain, with no error.
Follow-up questions
- →How would you keep the first word for each letter instead?
- →What does
{len(w) for w in words}produce?
JuniorTheoryCommonWhat does the program print?
What does the program print?
3.5 3 -4 2
Open full question →Common mistakes
- ✗Expecting
-7 // 2to be-3, as if//dropped the fraction://rounds down toward minus infinity, so-3.5becomes-4. - ✗Expecting
-7 % 3to be-1: in Python the remainder takes the sign of the divisor, so it is2, since-7 // 3is-3and-3 * 3 + 2is-7.
Follow-up questions
- →What does
7 % -3evaluate to, and why? - →Why does
6 / 2print3.0rather than3?
JuniorTheoryCommonWhat does the program print?
What does the program print?
C
Open full question →Common mistakes
- ✗Expecting
A, as if Python picked the closest condition: it checks them top to bottom, andscore > 50is true first, so onlyCprints. - ✗Expecting both
CandA: once one branch of anif/elifchain runs, the rest are skipped, even when their conditions are also true.
Follow-up questions
- →How would you reorder the conditions so that
85printsA? - →What would print if
elifwere replaced with a secondif?
JuniorTheoryCommonWhat does the program print?
What does the program print?
False True
Open full question →Common mistakes
- ✗Expecting
0.1 + 0.2to equal0.3exactly: both are stored in binary with a tiny error, so the sum is0.30000000000000004. - ✗Expecting
roundnot to help:round(total, 2)gives the float closest to0.30, the same float the literal0.3gives, so they are equal.
Follow-up questions
- →What does
print(0.1 + 0.2)show? - →How would you compare two computed floats without
round?
JuniorTheoryCommonWhat does the program print?
What does the program print?
3x3.14=9.4
Open full question →Common mistakes
- ✗Expecting
3.1for{price:.2f}, as if2counted all digits:.2fmeans two digits after the decimal point, so3.14159shows as3.14. - ✗Expecting
9.0, as if aninttimes afloatdropped the fraction:qty * priceis about9.425, and.1fkeeps one decimal,9.4.
Follow-up questions
- →Does
{price:.2f}change the value stored inprice? - →How would you print a literal
{inside an f-string?
JuniorTheoryCommonWhat does the program print?
What does the program print?
2 8 3.0
Open full question →Common mistakes
- ✗Expecting
int(2.9)to round to3:int()drops the fractional part, soint(2.9)is2, not the nearest whole number3. - ✗Expecting
71, as if+ 1wrote the digit1after7:int("7")is the number7, and a number plus1is8.
Follow-up questions
- →What does
int("2.5")do, and how would you get2from that string? - →What does
print(int(-2.9))print?
JuniorTheoryCommonWhat does the program print?
What does the program print?
4 3
Open full question →Common mistakes
- ✗Expecting
3 3, as ifb = acopied the list: it gives the same list a second name, sob.append(4)adds toaas well. - ✗Expecting
4 4, as ifa[:]were another name fora: a full slice builds a new list, sockeeps its 3 elements.
Follow-up questions
- →What does
print(a is b, a is c)print? - →Name two other ways to copy a list besides
a[:].
JuniorTheoryCommonWhat happens when this program runs?
What happens when this program runs?
Prints start, then a TypeError
Common mistakes
- ✗Expecting
{[1, 2]: 'A'}: a dict key must be hashable, and alistcan change, so it is not. The line raisesTypeError: unhashable type: 'list'. - ✗Expecting nothing to print: the
TypeErrorhappens while the program runs, sostartprints first and the program stops atnames[point] = "A".
Follow-up questions
- →How would you change
pointso that it works as a key? - →Can a tuple that holds a list be a dict key?
JuniorTheoryCommonWhat does the program print?
What does the program print?
n p 6
Open full question →Common mistakes
- ✗Expecting an
IndexErrorforword[-6]: negative indices run from-1down to-len(word), so-6is the first character of a 6-letter word. - ✗Expecting
5forlen(word), as if it gave the last index:lencounts the characters,6, while the last index is5, which is-1.
Follow-up questions
- →What does
word[-7]do? - →How would you print the second-to-last character of any string?
JuniorTheoryCommonWhat does the program print?
What does the program print?
[[5, 0], [5, 0]]
Open full question →Common mistakes
- ✗Expecting
[[5, 0], [0, 0]], as if* 2built two rows: the outer* 2repeats a reference to one inner list, so both rows are the same list. - ✗Expecting every cell to become
5:[0] * 2repeats the number0, and assigninggrid[0][0]replaces one cell of the row, not the other.
Follow-up questions
- →How would you build a 2 × 2 grid whose rows are independent lists?
- →What does
print(grid[0] is grid[1])print here?
JuniorTheoryCommonWhat does the program print?
What does the program print?
True False 1
Open full question →Common mistakes
- ✗Expecting
(1)to be a tuple: parentheses around one value only group it, soais theint1anda == 1isTrue. The comma is what makes a tuple. - ✗Expecting
(1,)to equal1:bis a tuple holding one element, and a tuple never equals anint, sob == 1isFalseandlen(b)is1.
Follow-up questions
- →How do you write an empty tuple, and what is its length?
- →What does
x = 1, 2assign tox?
JuniorTheoryCommonWhat does the program print?
What does the program print?
zero 4 True
Open full question →Common mistakes
- ✗Expecting
orandandto giveTrueorFalse: they return one of their operands, so0 or "zero"iszeroand3 and 4is4. - ✗Expecting
3 and 4to be3:andreturns its first false operand or, when both are true, the last one, so the result is4.
Follow-up questions
- →What does
print("" or 0)print, and why? - →Why does
notalways returnTrueorFalse, unlikeor?
JuniorTheoryCommonWhat does the program print?
What does the program print?
6 5
Open full question →Common mistakes
- ✗Expecting
6 6, as ifyfollowedx:y = xbindsyto the object5, andx = x + 1makes onlyxrefer to a new object6. - ✗Expecting
x = x + 1to change nothing, read as an equation:=is assignment, so Python computesx + 1and bindsxto the result6.
Follow-up questions
- →What does the program print if the third line is
x += 1instead? - →What would
print(y)show if the liney = xcame afterx = x + 1?
JuniorTheoryCommonWhat is a sequence in Python, and which built-in types are sequences?
What is a sequence in Python, and which built-in types are sequences?
A sequence is an ordered iterable with integer-index access via __getitem__ and a length via __len__. Built-ins: list, tuple, range, str, bytes. It supports indexing, slicing, in, len(), and iteration.
Common mistakes
- ✗Confusing 'sequence' with 'iterable' — sets and dicts are iterable yet not sequences
- ✗Assuming a sequence must be mutable —
tuple,str,rangeare immutable sequences - ✗Forgetting that
strandbytesare sequences, not opaque text blobs
Follow-up questions
- →How does slicing with a negative step like
s[::-1]work? - →What must
__getitem__accept for an object to support slicing?
JuniorTheoryCommonWhat does the program print?
What does the program print?
4 2 [1]
Open full question →Common mistakes
- ✗Expecting
6, as if|joined the sets end to end: a set holds each value once, soa | bis{1, 2, 3, 4}with4elements. - ✗Expecting
[1, 4]fora - b: the difference keeps what is inaand not inb; elements found only inbare dropped.
Follow-up questions
- →Which operator gives the elements that are in exactly one of the two sets?
- →Why does the program wrap
a - binsortedbefore printing?
JuniorTheoryCommonWhat does the program print?
What does the program print?
bcd ace ef
Open full question →Common mistakes
- ✗Expecting an
IndexErrorfors[4:100]: a slice clamps bounds past the end to the string's length, so it returns what is there,ef. - ✗Expecting
bcdefors[1:4], as if the stop were included: a slice stops beforestop, so it takes indices1,2and3.
Follow-up questions
- →What does
s[::-1]return? - →Why does
s[100]raise an error whiles[4:100]does not?
JuniorTheoryCommonWhat does the program print?
What does the program print?
None 1
Open full question →Common mistakes
- ✗Expecting
[1, 2, 3]inresult:sort()sorts the list in place and returnsNone; the function that returns a new sorted list issorted(). - ✗Expecting
numsto stay[3, 1, 2]:sort()changes the list it is called on, so after the callnums[0]is1.
Follow-up questions
- →How would you get a sorted copy and keep
numsunchanged? - →What does
print(nums.append(4))print?
JuniorTheoryCommonWhat does the program print?
What does the program print?
[2, 3] 2 3
Open full question →Common mistakes
- ✗Expecting
bto be4, as ifachanged first: the right sideb, a + bis computed in full from the old values, giving2, 3, before either name changes. - ✗Expecting a tuple
(2, 3): a starred target always collects its values into alist, whatever the right side is.
Follow-up questions
- →What does
first, *middle, last = [1, 2]put inmiddle? - →What happens with
a, b = [1, 2, 3]?
JuniorTheoryCommonWhat happens when this program runs?
What happens when this program runs?
Prints hello, then a TypeError
Common mistakes
- ✗Expecting
Jello, as if a string could be changed in place: strings are immutable, sos[0] = "J"raisesTypeErrorand the secondprintnever runs. - ✗Expecting the error before
hello: only a syntax error stops a file before it starts, and this one comes at its own line, after the firstprint.
Follow-up questions
- →How would you build the string
Jellofroms? - →Which part of the error message names the type that cannot be changed?
JuniorTheoryCommonWhat happens when this program runs?
What happens when this program runs?
A TypeError is raised
Common mistakes
- ✗Expecting
Age: 30, as if Python turned30into text:+never turns a number into text, so astrplus anintraisesTypeError; writestr(age). - ✗Expecting a
ValueError: that one comes from a bad value such asint("2.5"), while mixing astrand anintin+is a wrong type.
Follow-up questions
- →How would you change the second line so that it prints
Age: 30? - →What does
print("5" * 3)print, and why is it not an error?
JuniorCodeCommonWhat do these or / and expressions print?
What do these or / and expressions print?
(1) default — or returns the first truthy operand (an empty list is falsy). (2) 5 — the first truthy value. (3) '' — and returns the first falsy operand, or the last if all are truthy. or/and return one of the operands, not a bool — the basis of x = val or default.
Common mistakes
- ✗Believing
or/andalways return a boolean - ✗Thinking they always return the first operand regardless of truthiness
- ✗Not knowing the
x = val or defaultidiom relies on this
Follow-up questions
- →Why does short-circuit evaluation return an operand rather than a bool?
- →What is the risk of
x = val or defaultwhenvalcan be0or''?
JuniorTheoryCommonWhat does the program print?
What does the program print?
False True False True
Open full question →Common mistakes
- ✗Expecting
bool("0")to beFalse: a string is falsy only when it is empty, and"0"holds one character, so it is truthy. - ✗Expecting
bool(" ")to beFalse: a space is a character, so" "is not empty, and only the empty string""is falsy.
Follow-up questions
- →What does
bool(None)return? - →How would you check that a string holds something other than spaces?
JuniorTheoryCommonHow do a, b, c = seq and starred *rest unpacking work?
How do a, b, c = seq and starred *rest unpacking work?
The right side is iterated and bound positionally to the targets; the counts must match or you get a ValueError. A single starred target like a, *rest = seq absorbs the surplus into a list, so it can sit anywhere. It works on any iterable.
Common mistakes
- ✗Forgetting a count mismatch raises
ValueError, not a silent truncation - ✗Thinking
*restcollects into atupleinstead of alist - ✗Believing the starred target must be last rather than allowed anywhere
Follow-up questions
- →What does
a, *b, c = range(5)bind to each name? - →How does unpacking make the swap
a, b = b, awork without a temp?
JuniorTheoryCommonWhat do zip, enumerate, and range produce when iterating?
What do zip, enumerate, and range produce when iterating?
range(n) yields integers 0..n-1 lazily; enumerate(seq) yields (index, item) pairs; zip(a, b) yields tuples pairing elements until the shortest input ends. All three are lazy iterators in Python 3 — wrap in list() to materialize. zip(*rows) transposes.
Common mistakes
- ✗Expecting these to return lists rather than lazy iterators in Python 3
- ✗Thinking
zippads to the longest input instead of stopping at the shortest - ✗Assuming
enumeratestarts at 1 or thatrange(n)includesn
Follow-up questions
- →How do you make
enumeratestart counting from 1? - →What does
zip(*matrix)do, and why does it transpose?
MiddleTheoryCommonWhat do dict.items(), keys(), and values() return in Python 3?
What do dict.items(), keys(), and values() return in Python 3?
They return dynamic view objects, not lists. A view reflects later changes to the dict live, supports len(), iteration, and in, and the keys/items views behave set-like. Python 2 returned fresh lists here; Python 3 made views the default.
Common mistakes
- ✗Thinking
keys()returns a list — it's a live view in Python 3 - ✗Editing a dict while iterating its view — raises
RuntimeError - ✗Assuming a view is a one-shot iterator; it's re-iterable and supports
len()
Follow-up questions
- →Why might iterating a view while mutating the dict raise
RuntimeError? - →How are the keys and items views 'set-like'?
MiddleTheoryCommonWhat makes an object hashable in Python?
What makes an object hashable in Python?
An object is hashable if it has a stable __hash__ (constant over its lifetime) plus __eq__, and equal objects must hash equally. Immutable built-ins (int, str, a hashable tuple) qualify; mutable list, dict, set do not.
Common mistakes
- ✗Thinking any object with
__eq__is hashable — defining__eq__without__hash__makes it unhashable - ✗Believing a tuple is always hashable — it isn't if it contains a mutable element
- ✗Assuming mutable objects can be hashed as long as you avoid changing them
Follow-up questions
- →What happens to hashability if you define
__eq__but not__hash__? - →Why must equal objects have equal hashes?
MiddleCodeCommonWhat does this is vs == integer test print?
What does this is vs == integer test print?
True False then True True. CPython caches small integers from −5 to 256, so a and b are the same object (is → True); 257 is outside that range, so c and d are distinct objects (is → False). == compares value and is always True. Use is only for identity (e.g. x is None).
Common mistakes
- ✗Using
isto compare integer values instead of== - ✗Believing equal integers are always the same object
- ✗Thinking
==delegates tois
Follow-up questions
- →Why does CPython cache the integers from −5 to 256?
- →When is using
isthe correct choice instead of==?
MiddleTheoryCommonWhy is lookup faster in dict/set than in list/tuple?
Why is lookup faster in dict/set than in list/tuple?
dict and set are hash tables: lookup averages O(1) — Python hashes the key and jumps to a slot. list and tuple have no value index, so x in seq scans linearly at O(n). The trade-off: hashing needs hashable elements and more memory.
Common mistakes
- ✗Calling dict lookup O(log n) — it's average O(1) via hashing, not a tree search
- ✗Assuming a list
incheck is cheap — it's an O(n) linear scan - ✗Forgetting hash tables trade memory and hashability for that speed
Follow-up questions
- →What is the worst-case complexity of a dict lookup, and when does it occur?
- →Why can't you do an O(1) membership test on a
list?
MiddleTheoryCommonHow does Python compare two sequences with < and ==?
How does Python compare two sequences with < and ==?
Element by element, left to right (lexicographic). == is True only when lengths match and every pair is equal. For <, Python compares items at the first difference; if one side runs out first, the shorter is smaller. Items must be comparable or it raises TypeError.
Common mistakes
- ✗Thinking longer sequences are automatically greater
- ✗Assuming
==compares identity rather than element-wise value - ✗Expecting cross-type element comparison to coerce instead of raising
TypeError
Follow-up questions
- →How does
(1, 2) < (1, 2, 3)resolve when the shorter is a prefix of the longer? - →Why can comparing
[1, 'a']with[1, 2]raise only sometimes?
MiddleTheoryCommonHow does str differ from bytes, and what do encode/decode do?
How does str differ from bytes, and what do encode/decode do?
str is an immutable sequence of Unicode code points; bytes is an immutable sequence of raw 0–255 octets. encode turns a str into bytes through a codec like UTF-8; decode reverses it. Mixing them, or guessing the wrong codec, raises UnicodeError — Python 3 does no implicit conversion.
Common mistakes
- ✗Confusing the direction of
encode(str→bytes) anddecode(bytes→str) - ✗Expecting implicit str/bytes conversion like Python 2 did
- ✗Forgetting a wrong codec raises
UnicodeDecodeErrorrather than silently producing mojibake
Follow-up questions
- →Why does
'café'.encode('ascii')raise, and how do error handlers like'ignore'change that? - →How many bytes does a non-ASCII character take in UTF-8 versus UTF-16?
JuniorCodeOccasionalWhat does print(True + 4) output, and why?
What does print(True + 4) output, and why?
Prints 5, 2, 2. In Python bool is a subclass of int, with True == 1 and False == 0, so booleans participate directly in arithmetic. True + 4 is 1 + 4, and sum of booleans counts the True values. This is why summing a list of conditions counts how many are true.
Common mistakes
- ✗Thinking
boolis unrelated tointand cannot do arithmetic - ✗Expecting
True + 4to raise aTypeError - ✗Forgetting
sumof booleans counts theTruevalues
Follow-up questions
- →Why can
Trueand1collapse to one key in adict? - →What does
isinstance(True, int)return, and why?
JuniorCodeOccasionalWhat do these chained comparisons print?
What do these chained comparisons print?
All three print True. Python chains comparisons with an implicit and: (1) is 1 < 2 and 2 < 3; (2) is 3 > 2 and 2 == 2; (3) is (False == False) and (False in [False]) → True and True. The third surprises people because == and in chain together.
Common mistakes
- ✗Reading
a < b < cas(a < b) < cinstead of an implicitand - ✗Not realizing
==andinchain together in one expression - ✗Expecting a chained comparison to return an operand rather than a bool
Follow-up questions
- →How does Python evaluate the middle operand of a chain exactly once?
- →Why is
False == False in [False]not(False == False) in [False]?
JuniorTheoryOccasionalWhat is a hash collision?
What is a hash collision?
A hash collision is two different keys producing the same hash value, or landing in the same table slot. dict and set resolve it by probing other slots, so a collision costs lookup speed, not correctness — it never returns a wrong value.
Common mistakes
- ✗Thinking collisions make a dict return wrong values — they only add probing overhead
- ✗Believing built-in types never collide
- ✗Confusing equal hashes with equal keys — equal hash doesn't mean equal key
Follow-up questions
- →How does CPython's dict pick the next slot after a collision?
- →What happens to dict performance as the load factor rises?
JuniorCodeOccasionalAfter b = a on a dict, what do both print after edits?
After b = a on a dict, what do both print after edits?
Both print {1: 1, 2: 2, 3: 3}. b = a binds a second name to the same dict object — it does not copy it, so edits through either name are visible through both. To get an independent dict use a.copy() or dict(a) (a shallow copy). a is b is True here.
Common mistakes
- ✗Thinking
b = acopies the dict instead of aliasing it - ✗Expecting edits through one name not to affect the other
- ✗Confusing name binding with object duplication
Follow-up questions
- →How would you make
ban independent shallow copy ofa? - →What does
a is breturn here, and what does that test?
JuniorCodeOccasionalWhat does [1, 2, 3].extend('abc') produce, and why?
What does [1, 2, 3].extend('abc') produce, and why?
Prints [1, 2, 3, 'a', 'b', 'c']. extend iterates its argument and appends each item; a string is iterable over its characters, so each char is added separately. To add the whole string as one element use l.append('abc'), which gives [1, 2, 3, 'abc'].
Common mistakes
- ✗Expecting
extendto add the string as one element likeappend - ✗Thinking
extendrejects anything that is not a list - ✗Forgetting a string is iterable over its characters
Follow-up questions
- →How would you append the whole string as a single element instead?
- →What does
[1, 2].extend(3)raise, and why?
JuniorCodeOccasionalConvert a list of one-element tuples into a list of strings
Convert a list of one-element tuples into a list of strings
Take the first element of each tuple: [t[0] for t in data]. A one-element tuple is written ('x',) with a trailing comma, so indexing [0] pulls the single value out. Equivalent forms are [v for (v,) in data] (tuple unpacking in the loop target) or list(map(lambda t: t[0], data)).
Common mistakes
- ✗Forgetting a one-element tuple needs
[0]to unwrap it - ✗Thinking
str(t)cleanly yields the inner value - ✗Confusing the outer list index with the inner tuple index
Follow-up questions
- →How would you flatten tuples that may hold more than one value?
- →Why does
('x')differ from('x',)?
JuniorCodeOccasionalWhat does 0.1 + 0.2 == 0.3 print?
What does 0.1 + 0.2 == 0.3 print?
False. 0.1 + 0.2 is 0.30000000000000004 because these values are not exactly representable in binary floating point, so the sum differs from the literal 0.3. Compare with a tolerance instead — math.isclose(0.1 + 0.2, 0.3).
Common mistakes
- ✗Assuming float literals are stored exactly
- ✗Comparing floats with
==instead of a tolerance - ✗Thinking Python uses decimal arithmetic for
floatby default
Follow-up questions
- →How does
math.isclosedecide whether two floats are equal enough? - →When should you reach for
decimal.Decimalorfractions.Fractioninstead?
JuniorCodeOccasionalHow do you print a literal {} inside an f-string?
How do you print a literal {} inside an f-string?
Double the braces: print(f'Curly brackets: {{}}') prints Curly brackets: {}. In an f-string {{ is an escaped literal { and }} a literal }; a single {...} is a replacement field that must contain an expression, so an empty {} raises a SyntaxError at compile time.
Common mistakes
- ✗Trying to escape braces with a backslash instead of doubling them
- ✗Thinking an empty
{}is allowed and silently skipped - ✗Confusing f-string
{{}}escaping withstr.formatdifferences
Follow-up questions
- →How would you embed
{x}literally next to an interpolatedx? - →When is the
SyntaxErrorfor an empty{}raised — at compile or run time?
JuniorCodeOccasionalWhat does negative indexing print for 'abyz'[-1]?
What does negative indexing print for 'abyz'[-1]?
Prints z then y. A negative index counts from the end: -1 is the last element, -2 the second-to-last. It is equivalent to var[len(var) - 1]. This works on any sequence — str, list, tuple — and an out-of-range negative index raises IndexError.
Common mistakes
- ✗Thinking
-1points at the first element instead of the last - ✗Believing negative indices raise
IndexErrorby default - ✗Forgetting negative indexing works on any sequence, not just strings
Follow-up questions
- →What does the slice
var[-2:]return for this string? - →When does a negative index actually raise
IndexError?
MiddleCodeOccasionalWhat do += and + print for these aliased lists?
What do += and + print for these aliased lists?
[1, 2, 3, 4] then [1, 2, 3]. += on a list calls __iadd__, mutating the object in place, so b (same object) sees it. a = a + [4] builds a new list and rebinds a, leaving b pointing at the original — mutation versus rebinding.
Common mistakes
- ✗Believing
+=and+behave identically on lists - ✗Thinking
b = acopies the list - ✗Not distinguishing in-place mutation from rebinding
Follow-up questions
- →What does
__iadd__return, and why does that matter for the rebinding? - →How would the result differ if
awere a tuple instead of a list?
MiddleCodeOccasionalWhat does [[0]*3]*3 print after a write?
What does [[0]*3]*3 print after a write?
[[1, 0, 0], [1, 0, 0], [1, 0, 0]]. The outer * 3 copies the reference to the same inner list three times, so mutating one row mutates all. Fix with a comprehension that builds independent rows: grid = [[0] * 3 for _ in range(3)].
Common mistakes
- ✗Believing
* 3deep-copies the inner list - ✗Confusing list multiplication with NumPy broadcasting
- ✗Not knowing the comprehension fix for independent rows
Follow-up questions
- →Why does
[[0] * 3 for _ in range(3)]build independent rows? - →Does the same aliasing trap apply to
[0] * 3of immutable ints?
MiddleCodeOccasionalWhat happens with t[0] += [100] on a tuple?
What happens with t[0] += [100] on a tuple?
(1) succeeds → ([1, 99], 2): the tuple is immutable, but the list it references is mutable. (2) is the famous trap: += mutates the list AND tries to reassign t[0], so it raises TypeError — yet the list is still mutated to [1, 99, 100]. The operation half-succeeds.
Common mistakes
- ✗Thinking tuple immutability protects the mutable objects it holds
- ✗Expecting line (2) to leave the list unchanged after the error
- ✗Believing the
TypeErrorrolls back the in-place mutation
Follow-up questions
- →Why does
+=both mutate the list and attempt an assignment tot[0]? - →What does this reveal about whether a tuple is truly 'immutable'?
MiddleDebuggingRareWhy does this average return the wrong value?
Why does this average return the wrong value?
// is floor division, so average([1, 2]) returns 1, not 1.5. An empty list also raises ZeroDivisionError. Fix: use / for true division and guard the empty case: return sum(nums) / len(nums) if nums else 0.
Common mistakes
- ✗Confusing
//(floor) with/(true division) - ✗Thinking
//rounds to nearest rather than toward negative infinity - ✗Forgetting the empty-list ZeroDivisionError
Follow-up questions
- →What does
//do for negative operands like-7 // 2? - →How do you compute an integer average while still rounding correctly?
MiddleDebuggingRareWhy does this word counter raise KeyError?
Why does this word counter raise KeyError?
counts[w] += 1 reads counts[w] before the key exists → KeyError on the first sight of each word. Fix: counts[w] = counts.get(w, 0) + 1, or use collections.defaultdict(int), or simply collections.Counter(text.split()).
Common mistakes
- ✗Assuming
dict[k] += 1auto-initializes a missing key to 0 - ✗Blaming
splitor the return rather than the missing-key read - ✗Not reaching for
defaultdictorCounter
Follow-up questions
- →How does
collections.defaultdict(int)change what happens on a missing key? - →When is
dict.setdefaultpreferable togetfor this pattern?
MiddleDebuggingRareWhy does removing items while iterating skip elements?
Why does removing items while iterating skip elements?
Removing items during iteration shifts the indices under the iterator, so it skips elements. Never mutate a list while iterating it. Fix: iterate over a copy (for n in nums[:]:), or rebuild with a comprehension: nums = [n for n in nums if n % 2].
Common mistakes
- ✗Assuming removal leaves the iterator aligned with the next element
- ✗Expecting a RuntimeError on
remove(that fires for size change, but indices still shift) - ✗Mutating the list in place instead of iterating a copy or rebuilding
Follow-up questions
- →Why does iterating over
nums[:]make the removal safe? - →How does a comprehension avoid mutating the list at all?
MiddleDebuggingRareWhy is this string-building loop slow on large input?
Why is this string-building loop slow on large input?
Strings are immutable, so each s += ... builds a brand-new string — O(n²) for n words, slow on large inputs. It is functionally correct, but the complexity is the bug. Fix: return ' '.join(words), which is O(n) and idiomatic.
Common mistakes
- ✗Believing
+=on a string mutates a buffer in place - ✗Blaming the trailing space or
stripinstead of repeated allocation - ✗Micro-optimizing the concatenation instead of using
join
Follow-up questions
- →Why does string immutability force a new allocation on each
+=? - →When is
io.StringIOor a list-plus-joinpreferable for building text?
SeniorTheoryRareHow does CPython's dict store entries and handle resizing?
How does CPython's dict store entries and handle resizing?
A CPython dict is a sparse hash table; each slot holds the key's hash plus key and value refs. Lookup takes the hash's low bits as a slot offset and probes on collision. About a third of slots stay empty; when full it grows and entries reinsert.
Common mistakes
- ✗Thinking CPython dict uses separate chaining — it uses open addressing with probing
- ✗Believing the table never resizes — it grows once it gets too full
- ✗Assuming the full hash indexes the slot — only the low bits do, initially
Follow-up questions
- →Why keep a third of the slots empty instead of filling the table densely?
- →How did the 3.6 'compact dict' change the layout and add insertion ordering?
SeniorTheoryRareWhat does {True: 'a', 1: 'b', 1.0: 'c'} evaluate to, and why?
What does {True: 'a', 1: 'b', 1.0: 'c'} evaluate to, and why?
It evaluates to {True: 'c'}. True, 1, 1.0 are equal (True == 1 == 1.0) and hash identically, so they're one key. The first inserted key (True) is kept, but each assignment overwrites the value — final value 'c'.
Common mistakes
- ✗Thinking different types mean different keys — equality plus equal hash collapses them
- ✗Believing the surviving key becomes the last one written — it's the first inserted
- ✗Forgetting that the value still updates on each re-assignment to an equal key
Follow-up questions
- →Which object identity does the surviving key have —
Trueor1? - →How could this collapse cause a real bug in a lookup table?