Interview Questions — Python Questions with Answers
Start with the study notes on Interview Questions (definitions, examples and a quick revision), then work through 30 practice questions, each with the correct answer and a clear explanation of why it is right. When you are ready, take this topic as an interactive quiz to test yourself.
0 easy · 1 medium · 29 hard · Part of Python: Master Python from basics to advanced - 20 topics.
Topic overview
Python Interview Questions – the classic traps, explained
1. How to answer “what is the output?”
Interviewers love short snippets with a surprise. Read slowly and ask:
What type is each value?
Which operator runs first?
Is something copied, or shared?
Is the value truthy, or actually equal?
2. 1. Mutable default arguments
A default list is created once and shared by every call.
def f(a, b=[]):
b.append(a)
return b
print(f(1))
print(f(2))
Output[1]
[1, 2]
Remember: Fix: use b=None, then if b is None: b = [] inside the function.
3. 2. Shared lists and +=
b = a shares one list, and += on a list changes it in place.
a = b = [1, 2]
a += [3]
print(a, b)
x = [1, 2, 3]
y = x[:] # a real copy
y.append(4)
print(x, y)
A tuple's items can't be replaced, but a list inside a tuple can still change.
t = (1, 2, [3, 4])
t[2].append(5)
print(t)
Output(1, 2, [3, 4, 5])
9. 8. Loop variables and late binding
for i in range(3):
pass
print(i) # the for-loop variable survives
funcs = [lambda: i for i in range(3)]
print([f() for f in funcs]) # all see the last i
Output2
[2, 2, 2]
10. 9. Strings and ranges
print("ab" in "banana", "ba" in "banana")
print("abc".find("d"), "abc".find("b"))
print(list(range(3, 0)), list(range(3, 0, -1)))
print("-".join(str(x) for x in range(3)))
OutputFalse True
-1 1
[] [3, 2, 1]
0-1-2
11. 10. Quick concept questions
List vs tuple: a list is mutable, a tuple is immutable (and hashable).
is vs ==: identity vs value; use is only for None.
sort() vs sorted(): in place (returns None) vs a new list.
*args / **kwargs: extra positional arguments as a tuple / keyword arguments as a dict.
Generator vs list: lazy and one-pass vs stored in memory and reusable.
Shallow vs deep copy: inner objects shared vs everything copied.
GIL: one thread runs Python code at a time; use multiprocessing for CPU work.
__str__ vs __repr__: a friendly view vs the developer view (used in lists).
12. Common misconceptions
Misconception
Correct understanding
f(2) after f(1) returns [2] when the default is [].
The default list is shared, so it returns [1, 2].
[] == False is True.
An empty list is falsy but not equal to False.
2 ** 3 ** 2 is 64.
It is 2 ** 9 = 512.
A tuple can never change at all.
Its items can't be replaced, but a list inside it can change.
After for i in range(3), i is undefined.
The for-loop variable stays, holding 2.
13. Quick revision
Mutable default
Shared between calls; use None.
Aliasing
b = a shares one object.
Right-associative **
2 ** 3 ** 2 = 2 ** 9.
bool is int
True == 1, False == 0.
Truthy vs equal
bool([]) is False, but [] != False.
finally
Runs even after return; its return wins.
Shallow immutability
A tuple's inner list can change.
Late binding
Closures see a variable's latest value.
14. Test your understanding
What does print(0.1 + 0.2 == 0.3) print?
Show answer
False
What does print(2 ** 3 ** 2) print?
Show answer
512
a = b = [1]; a += [2]. What is b?
Show answer
[1, 2]
What is True + True?
Show answer
2
What does [] == False give?
Show answer
False
try: return 1, finally: return 2. What is returned?
Show answer
2
What is i after for i in range(3): pass?
Show answer
2
What is bool("False")?
Show answer
True (a non-empty string).
Key takeaway: Most Python “trick” questions come down to five ideas: shared mutable objects, operator precedence, bool being an int, truthiness vs equality, and late binding.
Notes written by Velnov. Every code example with an output was run in Python 3 to check that the output shown is exactly what Python prints.
Key concepts in this topic
Mutable defaultsCommon trapsFloating pointiscloseReferencesAliasingOperator precedenceAssociativitybool as intTruthinessTruthiness vs equalityComprehensionsList concatenationSlicingReverseList equalityin operatorSubstringstry/finallyreturnString operationsCopyingGotchasTuple immutability
A slice with step -1 walks the string backwards, reversing it: 'olleh'. The idiomatic Python reverse - also used to check palindromes with s == s[::-1].
SlicingReverse
Hard questions (29)
Q1.hard
What is the output?
def f(a, b=[]):
b.append(a)
return b
print(f(1))
print(f(2))
def f(a, b=None):
if b is None:
b = []
b.append(a)
return b
A [1] then [1, 2] - the default list is created ONCE and reused across calls
B [1] then [2]
C [1, 2] then [1, 2]
D Error
Answer: A — [1] then [1, 2] - the default list is created ONCE and reused across calls
The mutable default argument trap. The default [] is created once at definition and shared by every call that omits b, so it accumulates. Fix: use b=None and create the list inside.
Mutable defaultsCommon traps
Q2.hard
What is the output?
print(0.1 + 0.2 == 0.3)
import math
print(math.isclose(0.1 + 0.2, 0.3)) # True
A False - floating-point rounding makes 0.1 + 0.2 = 0.30000000000000004
B True
C Error
D 0.3
Answer: A — False - floating-point rounding makes 0.1 + 0.2 = 0.30000000000000004
Floats are binary approximations; 0.1 and 0.2 can't be represented exactly. Never compare floats with ==; use math.isclose(a, b) or round to a few decimals.
Floating pointisclose
Q3.hard
What is the output?
a = [1, 2, 3]
b = a
b.append(4)
print(a)
A [1, 2, 3, 4] - a and b are two names for the SAME list
B [1, 2, 3]
C [4]
D Error
Answer: A — [1, 2, 3, 4] - a and b are two names for the SAME list
b = a does not copy; both names reference one list. Mutating through b is visible through a. For an independent copy: b = a.copy() or b = a[:].
ReferencesAliasing
Q4.hard
What is the output?
print(2 ** 3 ** 2)
A 512 - ** is right-associative, so it's 2 ** (3 ** 2) = 2 ** 9
B 64 - (2 ** 3) ** 2
C 12
D 18
Answer: A — 512 - ** is right-associative, so it's 2 ** (3 ** 2) = 2 ** 9
Exponentiation groups right-to-left (unlike most operators). So 3 ** 2 = 9 first, then 2 ** 9 = 512. A favorite precedence gotcha.
Operator precedenceAssociativity
Q5.hard
What is the output?
print(True == 1, False == 0, True + True)
A True True 2 - bool is a subclass of int (True is 1, False is 0)
B False False 0
C True True True
D Error
Answer: A — True True 2 - bool is a subclass of int (True is 1, False is 0)
Booleans ARE integers under the hood: True equals 1, False equals 0, and they do arithmetic. That's why sum([True, False, True]) counts the Trues (2).
bool as intTruthiness
Q6.hard
What is the output?
print([] == False, bool([]) == False)
A False True - an empty list is falsy but NOT equal to False
B True True
C False False
D Error
Answer: A — False True - an empty list is falsy but NOT equal to False
[] == False compares a list to a boolean (different values) -> False. But bool([]) converts the empty list to False first -> True. Truthiness (used by if) is different from == equality.
Truthiness vs equality
Q7.hard
What is the output?
print([i for i in range(3)] + [i*2 for i in range(3)])
A [0, 1, 2, 0, 2, 4]
B [0, 1, 2, 2, 4, 6]
C [0, 2, 4]
D Error
Answer: A — [0, 1, 2, 0, 2, 4]
First comprehension: [0, 1, 2]. Second: [0, 2, 4]. + concatenates the two lists -> [0, 1, 2, 0, 2, 4].
ComprehensionsList concatenation
Q9.hard
What is the output?
print(1 == True, [1] == [True])
A True True - 1 equals True, so the lists compare equal too
B True False
C False False
D Error
Answer: A — True True - 1 equals True, so the lists compare equal too
1 == True is True (bool is int). List equality compares elements: 1 == True is True, so [1] == [True]. Consistent once you know True behaves as 1.
bool as intList equality
Q10.hard
What is the output?
print("a" in "banana", "ba" in "banana", "ab" in "banana")
A True True False - 'in' checks for a contiguous SUBSTRING
B True True True
C False False False
D Error
Answer: A — True True False - 'in' checks for a contiguous SUBSTRING
in tests substrings: 'a' and 'ba' appear in 'banana', but 'ab' does not (the letters aren't adjacent in that order). Substring, not subsequence.
in operatorSubstrings
Q11.hard
What is the output?
def f():
try:
return 1
finally:
return 2
print(f())
A 2 - finally's return OVERRIDES the try's return
B 1
C 1 then 2
D Error
Answer: A — 2 - finally's return OVERRIDES the try's return
finally always runs, and its return replaces the pending one from try. This is why returning inside finally is discouraged - it silently swallows the real result (and even exceptions).
try/finallyreturn
Q12.hard
What is the output?
print(3 * "ab" + "c")
A ababcabc
B abababc
C ababab c
D Error
Answer: B — abababc
* binds tighter than +: 3 * "ab" = "ababab" first, then + "c" -> "abababc". Precedence applies to string operators too.
Operator precedenceString operations
Q13.hard
What is the output?
x = [1, 2, 3]
y = x[:]
y.append(4)
print(x, y)
A [1, 2, 3] [1, 2, 3, 4] - x[:] made an independent copy
B [1, 2, 3, 4] [1, 2, 3, 4]
C [1, 2, 3] [4]
D Error
Answer: A — [1, 2, 3] [1, 2, 3, 4] - x[:] made an independent copy
x[:] creates a shallow copy, so y is a separate list. Appending to y leaves x unchanged. (Contrast with y = x, which would share the list.)
SlicingCopying
Q14.hard
What is the output?
print(bool("False"), bool("0"), bool(0), bool(""))
A True True False False
B False False False False
C True True True True
D False True False True
Answer: A — True True False False
Any NON-EMPTY string is truthy - even "False" and "0". Only the empty string is falsy. The number 0 is falsy. So: True, True, False, False.
TruthinessGotchas
Q15.hard
What is the output?
a = (1, 2, [3, 4])
a[2].append(5)
print(a)
A (1, 2, [3, 4, 5]) - the tuple is immutable but its inner LIST is mutable
B Error - tuples can't change
C (1, 2, [3, 4])
D (1, 2, 3, 4, 5)
Answer: A — (1, 2, [3, 4, 5]) - the tuple is immutable but its inner LIST is mutable
A tuple can't have its elements REPLACED, but if an element is itself mutable (a list), that object can still be modified. Immutability is shallow.
Tuple immutabilityMutable elements
Q16.hard
What is the output?
print(len("café"))
A 4 - Python 3 strings count CHARACTERS, not bytes
B 5
C 3
D Error
Answer: A — 4 - Python 3 strings count CHARACTERS, not bytes
In Python 3, strings are sequences of Unicode characters, so 'café' has length 4 regardless of how many bytes it takes to encode. (Encoded as UTF-8 bytes it would be 5.)
Unicodelen()
Q17.hard
What is the output?
print(list(range(0)), list(range(5, 5)), list(range(3, 0)))
A [] [] [] - all three ranges are empty
B [0] [5] [3, 2, 1]
C Error
D [] [] [3, 2, 1]
Answer: A — [] [] [] - all three ranges are empty
range(0) and range(5,5) produce nothing. range(3, 0) is also empty because it counts UP by default and 3 is already past 0 - you'd need range(3, 0, -1) to count down.
range()Empty ranges
Q18.hard
What is the output?
print("5" + "5", "5" * 2, int("5") + 5)
A 55 55 10
B 10 10 10
C 55 25 10
D Error
Answer: A — 55 55 10
"5" + "5" joins strings -> "55". "5" * 2 repeats -> "55". int("5") + 5 does math -> 10. Same-looking data behaving three ways depending on type.
Type behaviorOperators
Q19.hard
What is the output?
x = {1, 2, 3}
y = {3, 4, 5}
print(x & y, x | y, x ^ y)
A {3} {1,2,3,4,5} {1,2,4,5}
B {3} {3} {3}
C {1,2} {4,5} {3}
D Error
Answer: A — {3} {1,2,3,4,5} {1,2,4,5}
& intersection (common: {3}), | union (all: {1,2,3,4,5}), ^ symmetric difference (in one but not both: {1,2,4,5}).
Set operations
Q20.hard
What is the output?
nums = [1, 2, 3, 4]
print(nums[::2], nums[1::2], nums[::-2])
A [1, 3] [2, 4] [4, 2]
B [1, 2] [3, 4] [4, 3]
C [2, 4] [1, 3] [1, 3]
D Error
Answer: A — [1, 3] [2, 4] [4, 2]
[::2] every other from start -> [1,3]. [1::2] every other from index 1 -> [2,4]. [::-2] every other going backwards -> [4,2].
SlicingStep
Q21.hard
What is the output?
print(sorted([3, 1, 2]) is sorted([3, 1, 2]))
A False - sorted() returns a NEW list each call, so they're different objects
B True
C Error
D [1, 2, 3]
Answer: A — False - sorted() returns a NEW list each call, so they're different objects
Both calls produce equal lists ([1,2,3]), but they're separate objects in memory, so 'is' (identity) is False. == would be True. Classic is-vs-== distinction.
is vs ==sorted()
Q22.hard
What is the output?
d = {"a": 1, "b": 2}
print([k for k in d], [v for v in d.values()])
A ['a', 'b'] [1, 2]
B [1, 2] ['a', 'b']
C ['a', 'b'] ['a', 'b']
D Error
Answer: A — ['a', 'b'] [1, 2]
Iterating a dict directly gives its KEYS (['a', 'b']); .values() gives the values ([1, 2]). A frequent point of confusion when looping dicts.
Dict iterationkeys/values
Q23.hard
What is the output?
print("abc".find("d"), "abc".find("b"))
A -1 1 - find returns -1 when not found, else the index
B None 1
C Error 1
D 0 1
Answer: A — -1 1 - find returns -1 when not found, else the index
find returns the index of the substring, or -1 if absent. 'd' isn't there (-1); 'b' is at index 1. (index() would raise ValueError instead of returning -1.)
find()index()
Q24.hard
What is the output?
print(list(map(lambda x: x**2, filter(lambda x: x % 2, range(6)))))
A [1, 9, 25] - keep odds (1,3,5), then square them
B [0, 4, 16]
C [1, 4, 9, 16, 25]
D [0, 1, 4, 9, 16, 25]
Answer: A — [1, 9, 25] - keep odds (1,3,5), then square them
filter keeps values where x % 2 is truthy (odd): 1, 3, 5. map squares them: 1, 9, 25. Read inside-out. A comprehension would be [x**2 for x in range(6) if x % 2].
map()filter()lambda
Q25.hard
What is the output?
a = b = [1, 2]
a += [3]
print(a, b)
A [1, 2, 3] [1, 2, 3] - += mutates the shared list in place
B [1, 2, 3] [1, 2]
C [1, 2] [1, 2, 3]
D Error
Answer: A — [1, 2, 3] [1, 2, 3] - += mutates the shared list in place
a and b point to the same list. For lists, += calls extend (in-place mutation), so both names see [1, 2, 3]. (For an int, += would rebind and NOT affect the other name.)
AliasingIn-place operators
Q26.hard
What is the output?
print("-".join([str(x) for x in range(3)]))
A 0-1-2
B 012
C 0 1 2
D ['0-1-2']
Answer: A — 0-1-2
The comprehension makes ['0', '1', '2'] (join needs strings, not ints), then join glues them with '-' -> '0-1-2'.
join()Comprehensions
Q27.hard
What is the output?
x = 1
print(x, end="")
print(x + 1)
A 12 (on one line) - end="" removes the newline after the first print
B 1 then 2 on separate lines
C 1 2
D 3
Answer: A — 12 (on one line) - end="" removes the newline after the first print
end="" replaces the default trailing newline with nothing, so the second print continues on the same line: '1' then '2' -> '12'.
print endOutput formatting
Q28.hard
What is the output?
print(max("apple", "banana", key=len), max("apple", "banana"))
A banana banana - one by LENGTH, the other alphabetically (b > a)
B apple apple
C banana apple
D apple banana
Answer: A — banana banana - one by LENGTH, the other alphabetically (b > a)
With key=len, max compares lengths: 'banana' (6) wins. Without key, max compares alphabetically: 'banana' > 'apple' because 'b' > 'a'. Both give 'banana' here, for different reasons.
max()key functions
Q29.hard
What is the output?
print(type([]) == list, type([]) is list, isinstance([], list))
A True True True - all three confirm [] is a list
B True False True
C False False True
D Error
Answer: A — True True True - all three confirm [] is a list
type([]) is the list class. == compares it to list (True); is checks identity - and classes are singletons, so True; isinstance is the preferred check (True). All three agree here, but isinstance is best because it respects subclasses.
type()isinstance()
Q30.hard
The famous one - what is the output?
for i in range(3):
pass
print(i)
A 2 - the loop variable survives with its LAST value after the loop
B 3
C NameError
D 0
Answer: A — 2 - the loop variable survives with its LAST value after the loop
Unlike comprehensions, a for-loop variable leaks into the surrounding scope and keeps its final value (2, the last item of range(3)). If range were empty, i would never be defined.