Python Interview Questions — Coding & Theory
The most-asked Python interview content at Google, Amazon, Meta, Apple, Microsoft, and Netflix — 100 coding problems mapped against the canonical Blind 75 / NeetCode 150, plus 195 theory questions covering Python internals, OOP, decorators, generators, the GIL, async, exceptions, modules, and the standard library.
-
#1
Two Sum
EasyApproach: Hash map: for each number n, check if target - n was already seen.
▼ Show solution ▲ Hide solution
pythondef twoSum(nums, target): seen = {} for i, n in enumerate(nums): if target - n in seen: return [seen[target - n], i] seen[n] = iOutput Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#2
Contains Duplicate
EasyApproach: Compare set size to list size.
▼ Show solution ▲ Hide solution
pythondef containsDuplicate(nums): return len(set(nums)) != len(nums)Output Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#3
Valid Anagram
EasyApproach: Compare character counts.
▼ Show solution ▲ Hide solution
pythonfrom collections import Counter def isAnagram(s, t): return Counter(s) == Counter(t)Output Idle⏱ Time O(n) · Space O(1) (26 letters)Open on LeetCode ↗ -
#4
Group Anagrams
MediumApproach: Group by sorted-letter tuple (or 26-int count tuple) as dict key.
▼ Show solution ▲ Hide solution
pythonfrom collections import defaultdict def groupAnagrams(strs): groups = defaultdict(list) for s in strs: groups[tuple(sorted(s))].append(s) return list(groups.values())Output Idle⏱ Time O(n·k log k) · Space O(n·k)Open on LeetCode ↗ -
#5
Top K Frequent Elements
MediumApproach: Counter + most_common, or bucket sort by frequency for O(n).
▼ Show solution ▲ Hide solution
pythonfrom collections import Counter def topKFrequent(nums, k): return [n for n, _ in Counter(nums).most_common(k)]Output Idle⏱ Time O(n log k) · Space O(n)Open on LeetCode ↗ -
#6
Product of Array Except Self
MediumApproach: Two passes: prefix products left-to-right, then multiply by suffix products right-to-left.
▼ Show solution ▲ Hide solution
pythondef productExceptSelf(nums): n = len(nums) out = [1] * n left = 1 for i in range(n): out[i] = left left *= nums[i] right = 1 for i in range(n - 1, -1, -1): out[i] *= right right *= nums[i] return outOutput Idle⏱ Time O(n) · Space O(1) extraOpen on LeetCode ↗ -
#7
Valid Sudoku
MediumApproach: One set tracking (row,val), (val,col), (box,val) — reject on duplicate.
▼ Show solution ▲ Hide solution
pythondef isValidSudoku(board): seen = set() for r in range(9): for c in range(9): v = board[r][c] if v == '.': continue for k in ((r, v), (v, c), (r // 3, c // 3, v)): if k in seen: return False seen.add(k) return TrueOutput Idle⏱ Time O(1) (81 cells) · Space O(1)Open on LeetCode ↗ -
#8
Encode and Decode Strings
MediumApproach: Prefix each string with its length + delimiter; decode by reading the length.
▼ Show solution ▲ Hide solution
pythonclass Codec: def encode(self, strs): return ''.join(f'{len(s)}#{s}' for s in strs) def decode(self, s): out, i = [], 0 while i < len(s): j = s.index('#', i) n = int(s[i:j]) out.append(s[j + 1:j + 1 + n]) i = j + 1 + n return outOutput Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#9
Longest Consecutive Sequence
MediumApproach: Use a set. Only start counting from numbers whose predecessor is not present.
▼ Show solution ▲ Hide solution
pythondef longestConsecutive(nums): s = set(nums) best = 0 for n in s: if n - 1 not in s: cur = n while cur + 1 in s: cur += 1 best = max(best, cur - n + 1) return bestOutput Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#10
Best Time to Buy and Sell Stock
EasyApproach: Track running minimum; profit = max(profit, price - minSoFar).
▼ Show solution ▲ Hide solution
pythondef maxProfit(prices): low, profit = float('inf'), 0 for p in prices: low = min(low, p) profit = max(profit, p - low) return profitOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#11
Valid Palindrome
EasyApproach: Filter alphanumerics, lowercase, compare to its reverse.
▼ Show solution ▲ Hide solution
pythondef isPalindrome(s): cleaned = [c.lower() for c in s if c.isalnum()] return cleaned == cleaned[::-1]Output Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#12
Two Sum II — Input Array Is Sorted
MediumApproach: Two pointers from both ends; move based on sum vs. target.
▼ Show solution ▲ Hide solution
pythondef twoSum(nums, target): l, r = 0, len(nums) - 1 while l < r: s = nums[l] + nums[r] if s == target: return [l + 1, r + 1] if s < target: l += 1 else: r -= 1Output Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#13
3Sum
MediumApproach: Sort, then fix one number and two-pointer the rest. Skip duplicates.
▼ Show solution ▲ Hide solution
pythondef threeSum(nums): nums.sort() res = [] for i, a in enumerate(nums): if i and a == nums[i - 1]: continue l, r = i + 1, len(nums) - 1 while l < r: s = a + nums[l] + nums[r] if s < 0: l += 1 elif s > 0: r -= 1 else: res.append([a, nums[l], nums[r]]) l += 1 while l < r and nums[l] == nums[l - 1]: l += 1 return resOutput Idle⏱ Time O(n²) · Space O(1) (excluding output)Open on LeetCode ↗ -
#14
Container With Most Water
MediumApproach: Two pointers — always move the smaller side inward.
▼ Show solution ▲ Hide solution
pythondef maxArea(h): l, r, best = 0, len(h) - 1, 0 while l < r: best = max(best, (r - l) * min(h[l], h[r])) if h[l] < h[r]: l += 1 else: r -= 1 return bestOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#15
Trapping Rain Water
HardApproach: Two pointers; water at each index = max-so-far on that side minus its height.
▼ Show solution ▲ Hide solution
pythondef trap(h): l, r = 0, len(h) - 1 lmax = rmax = total = 0 while l < r: if h[l] < h[r]: lmax = max(lmax, h[l]) total += lmax - h[l] l += 1 else: rmax = max(rmax, h[r]) total += rmax - h[r] r -= 1 return totalOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#16
Best Time to Buy and Sell Stock II
MediumApproach: Sum every positive day-over-day delta.
▼ Show solution ▲ Hide solution
pythondef maxProfit(prices): return sum(max(prices[i + 1] - prices[i], 0) for i in range(len(prices) - 1))Output Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#17
Longest Substring Without Repeating Characters
MediumApproach: Sliding window. When a repeat appears inside the window, jump the left edge past it.
▼ Show solution ▲ Hide solution
pythondef lengthOfLongestSubstring(s): seen = {} l = best = 0 for r, c in enumerate(s): if c in seen and seen[c] >= l: l = seen[c] + 1 seen[c] = r best = max(best, r - l + 1) return bestOutput Idle⏱ Time O(n) · Space O(min(n, alphabet))Open on LeetCode ↗ -
#18
Longest Repeating Character Replacement
MediumApproach: Window valid if (size − mostFrequent) ≤ k; shrink left when invalid.
▼ Show solution ▲ Hide solution
pythonfrom collections import Counter def characterReplacement(s, k): cnt = Counter() l = best = top = 0 for r, c in enumerate(s): cnt[c] += 1 top = max(top, cnt[c]) if r - l + 1 - top > k: cnt[s[l]] -= 1 l += 1 best = max(best, r - l + 1) return bestOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#19
Permutation in String
MediumApproach: Maintain a frequency window of size len(s1) over s2 and compare counters.
▼ Show solution ▲ Hide solution
pythonfrom collections import Counter def checkInclusion(s1, s2): if len(s1) > len(s2): return False need = Counter(s1) have = Counter(s2[:len(s1)]) if have == need: return True for i in range(len(s1), len(s2)): have[s2[i]] += 1 have[s2[i - len(s1)]] -= 1 if have[s2[i - len(s1)]] == 0: del have[s2[i - len(s1)]] if have == need: return True return FalseOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#20
Minimum Window Substring
HardApproach: Expand right until all needed chars covered, then shrink left while still valid.
▼ Show solution ▲ Hide solution
pythonfrom collections import Counter def minWindow(s, t): need = Counter(t) missing = len(t) l = best_l = 0 best = '' for r, c in enumerate(s): if need[c] > 0: missing -= 1 need[c] -= 1 if missing == 0: while need[s[l]] < 0: need[s[l]] += 1 l += 1 if not best or r - l + 1 < len(best): best = s[l:r + 1] need[s[l]] += 1 missing += 1 l += 1 return bestOutput Idle⏱ Time O(n) · Space O(k) where k = |t|Open on LeetCode ↗ -
#21
Sliding Window Maximum
HardApproach: Monotonic deque holding indices of candidates in decreasing order of value.
▼ Show solution ▲ Hide solution
pythonfrom collections import deque def maxSlidingWindow(nums, k): dq, out = deque(), [] for i, n in enumerate(nums): while dq and nums[dq[-1]] < n: dq.pop() dq.append(i) if dq[0] <= i - k: dq.popleft() if i >= k - 1: out.append(nums[dq[0]]) return outOutput Idle⏱ Time O(n) · Space O(k)Open on LeetCode ↗ -
#22
Valid Parentheses
EasyApproach: Stack: push openers, pop and match on closers.
▼ Show solution ▲ Hide solution
pythondef isValid(s): pairs = {')': '(', ']': '[', '}': '{'} stack = [] for c in s: if c in pairs: if not stack or stack.pop() != pairs[c]: return False else: stack.append(c) return not stackOutput Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#23
Min Stack
MediumApproach: Store (value, currentMin) pairs so getMin is O(1).
▼ Show solution ▲ Hide solution
pythonclass MinStack: def __init__(self): self.stack = [] def push(self, v): m = v if not self.stack else min(v, self.stack[-1][1]) self.stack.append((v, m)) def pop(self): self.stack.pop() def top(self): return self.stack[-1][0] def getMin(self): return self.stack[-1][1]Output Idle⏱ All ops O(1) · Space O(n)Open on LeetCode ↗ -
#24
Evaluate Reverse Polish Notation
MediumApproach: Push numbers; on operator, pop b then a, push a OP b. Use int(a/b) for truncated division.
▼ Show solution ▲ Hide solution
pythondef evalRPN(tokens): stack = [] for t in tokens: if t in {'+', '-', '*', '/'}: b, a = stack.pop(), stack.pop() if t == '+': stack.append(a + b) elif t == '-': stack.append(a - b) elif t == '*': stack.append(a * b) else: stack.append(int(a / b)) else: stack.append(int(t)) return stack[0]Output Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#25
Generate Parentheses
MediumApproach: Backtrack — add '(' if open < n, add ')' if close < open.
▼ Show solution ▲ Hide solution
pythondef generateParenthesis(n): out = [] def bt(s, o, c): if len(s) == 2 * n: out.append(s) return if o < n: bt(s + '(', o + 1, c) if c < o: bt(s + ')', o, c + 1) bt('', 0, 0) return outOutput Idle⏱ Time O(4ⁿ / √n) · Space O(n)Open on LeetCode ↗ -
#26
Daily Temperatures
MediumApproach: Monotonic decreasing stack of indices; on warmer day, pop and record distance.
▼ Show solution ▲ Hide solution
pythondef dailyTemperatures(t): out = [0] * len(t) stack = [] for i, v in enumerate(t): while stack and t[stack[-1]] < v: j = stack.pop() out[j] = i - j stack.append(i) return outOutput Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#27
Car Fleet
MediumApproach: Sort by position desc. Walk forward; a car only forms a new fleet if its arrival time > current max.
▼ Show solution ▲ Hide solution
pythondef carFleet(target, position, speed): pairs = sorted(zip(position, speed), reverse=True) fleets, last = 0, 0 for p, s in pairs: t = (target - p) / s if t > last: last = t fleets += 1 return fleetsOutput Idle⏱ Time O(n log n) · Space O(n)Open on LeetCode ↗ -
#28
Largest Rectangle in Histogram
HardApproach: Monotonic increasing stack of (index, height); pop while current height is smaller.
▼ Show solution ▲ Hide solution
pythondef largestRectangleArea(h): stack, best = [], 0 for i, v in enumerate(h + [0]): start = i while stack and stack[-1][1] > v: j, hh = stack.pop() best = max(best, hh * (i - j)) start = j stack.append((start, v)) return bestOutput Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#29
Binary Search
EasyApproach: Standard iterative binary search.
▼ Show solution ▲ Hide solution
pythondef search(nums, target): l, r = 0, len(nums) - 1 while l <= r: m = (l + r) // 2 if nums[m] == target: return m if nums[m] < target: l = m + 1 else: r = m - 1 return -1Output Idle⏱ Time O(log n) · Space O(1)Open on LeetCode ↗ -
#30
Search a 2D Matrix
MediumApproach: Treat the matrix as a 1D sorted array of length rows·cols.
▼ Show solution ▲ Hide solution
pythondef searchMatrix(m, target): rows, cols = len(m), len(m[0]) l, r = 0, rows * cols - 1 while l <= r: mid = (l + r) // 2 v = m[mid // cols][mid % cols] if v == target: return True if v < target: l = mid + 1 else: r = mid - 1 return FalseOutput Idle⏱ Time O(log(rows·cols)) · Space O(1)Open on LeetCode ↗ -
#31
Koko Eating Bananas
MediumApproach: Binary search on speed k in [1, max(piles)]; check feasibility in O(n).
▼ Show solution ▲ Hide solution
pythonfrom math import ceil def minEatingSpeed(piles, h): l, r = 1, max(piles) while l < r: m = (l + r) // 2 if sum(ceil(p / m) for p in piles) <= h: r = m else: l = m + 1 return lOutput Idle⏱ Time O(n log M) · Space O(1)Open on LeetCode ↗ -
#32
Find Minimum in Rotated Sorted Array
MediumApproach: Compare mid to right; if mid > right, min is to the right of mid.
▼ Show solution ▲ Hide solution
pythondef findMin(nums): l, r = 0, len(nums) - 1 while l < r: m = (l + r) // 2 if nums[m] > nums[r]: l = m + 1 else: r = m return nums[l]Output Idle⏱ Time O(log n) · Space O(1)Open on LeetCode ↗ -
#33
Search in Rotated Sorted Array
MediumApproach: Decide which half is sorted, then check if target is within that half.
▼ Show solution ▲ Hide solution
pythondef search(nums, target): l, r = 0, len(nums) - 1 while l <= r: m = (l + r) // 2 if nums[m] == target: return m if nums[l] <= nums[m]: if nums[l] <= target < nums[m]: r = m - 1 else: l = m + 1 else: if nums[m] < target <= nums[r]: l = m + 1 else: r = m - 1 return -1Output Idle⏱ Time O(log n) · Space O(1)Open on LeetCode ↗ -
#34
Time Based Key-Value Store
MediumApproach: Store each key's appends sorted by timestamp; binary search the largest ≤ t.
▼ Show solution ▲ Hide solution
pythonfrom collections import defaultdict class TimeMap: def __init__(self): self.data = defaultdict(list) def set(self, k, v, t): self.data[k].append((t, v)) def get(self, k, t): arr = self.data[k] l, r, res = 0, len(arr) - 1, '' while l <= r: m = (l + r) // 2 if arr[m][0] <= t: res = arr[m][1] l = m + 1 else: r = m - 1 return resOutput Idle⏱ set O(1) · get O(log n)Open on LeetCode ↗ -
#35
Median of Two Sorted Arrays
HardApproach: Binary-search a partition of the shorter array so all left ≤ all right.
▼ Show solution ▲ Hide solution
pythondef findMedianSortedArrays(a, b): if len(a) > len(b): a, b = b, a m, n = len(a), len(b) half = (m + n + 1) // 2 l, r = 0, m while l <= r: i = (l + r) // 2 j = half - i al = a[i - 1] if i else -float('inf') ar = a[i] if i < m else float('inf') bl = b[j - 1] if j else -float('inf') br = b[j] if j < n else float('inf') if al <= br and bl <= ar: if (m + n) % 2: return max(al, bl) return (max(al, bl) + min(ar, br)) / 2 if al > br: r = i - 1 else: l = i + 1Output Idle⏱ Time O(log min(m, n)) · Space O(1)Open on LeetCode ↗ -
#36
Reverse Linked List
EasyApproach: Iterate, repointing each node's next to previous.
▼ Show solution ▲ Hide solution
pythondef reverseList(head): prev = None while head: head.next, prev, head = prev, head, head.next return prevOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#37
Merge Two Sorted Lists
EasyApproach: Dummy head + tail pointer; splice smaller node each step.
▼ Show solution ▲ Hide solution
pythondef mergeTwoLists(a, b): dummy = tail = ListNode() while a and b: if a.val < b.val: tail.next, a = a, a.next else: tail.next, b = b, b.next tail = tail.next tail.next = a or b return dummy.nextOutput Idle⏱ Time O(n + m) · Space O(1)Open on LeetCode ↗ -
#38
Reorder List
MediumApproach: Find middle (fast/slow), reverse second half, then interleave.
▼ Show solution ▲ Hide solution
pythondef reorderList(head): slow = fast = head while fast and fast.next: slow, fast = slow.next, fast.next.next prev, cur = None, slow.next slow.next = None while cur: nxt = cur.next cur.next = prev prev = cur cur = nxt a, b = head, prev while b: a_nxt, b_nxt = a.next, b.next a.next = b b.next = a_nxt a, b = a_nxt, b_nxtOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#39
Remove Nth Node From End of List
MediumApproach: Two pointers; advance fast by n then move both until fast.next is None.
▼ Show solution ▲ Hide solution
pythondef removeNthFromEnd(head, n): dummy = ListNode(0, head) fast = slow = dummy for _ in range(n): fast = fast.next while fast.next: fast, slow = fast.next, slow.next slow.next = slow.next.next return dummy.nextOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#40
Copy List with Random Pointer
MediumApproach: Two-pass with old→new hash map.
▼ Show solution ▲ Hide solution
pythondef copyRandomList(head): mp = {None: None} cur = head while cur: mp[cur] = Node(cur.val) cur = cur.next cur = head while cur: mp[cur].next = mp[cur.next] mp[cur].random = mp[cur.random] cur = cur.next return mp[head]Output Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#41
Add Two Numbers
MediumApproach: Walk both lists adding digits + carry into a new list.
▼ Show solution ▲ Hide solution
pythondef addTwoNumbers(a, b): dummy = tail = ListNode() carry = 0 while a or b or carry: v = (a.val if a else 0) + (b.val if b else 0) + carry carry, v = divmod(v, 10) tail.next = ListNode(v) tail = tail.next if a: a = a.next if b: b = b.next return dummy.nextOutput Idle⏱ Time O(max(m, n)) · Space O(max(m, n))Open on LeetCode ↗ -
#42
Linked List Cycle
EasyApproach: Floyd's tortoise and hare — slow & fast pointers must meet inside a cycle.
▼ Show solution ▲ Hide solution
pythondef hasCycle(head): slow = fast = head while fast and fast.next: slow, fast = slow.next, fast.next.next if slow is fast: return True return FalseOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#43
Find the Duplicate Number
MediumApproach: Treat indices as a linked list; cycle entry is the duplicate (Floyd's).
▼ Show solution ▲ Hide solution
pythondef findDuplicate(nums): slow = fast = nums[0] while True: slow = nums[slow] fast = nums[nums[fast]] if slow == fast: break slow2 = nums[0] while slow != slow2: slow, slow2 = nums[slow], nums[slow2] return slowOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#44
LRU Cache
MediumApproach: OrderedDict — move accessed key to the end; pop the oldest on overflow.
▼ Show solution ▲ Hide solution
pythonfrom collections import OrderedDict class LRUCache: def __init__(self, capacity): self.cap = capacity self.cache = OrderedDict() def get(self, key): if key not in self.cache: return -1 self.cache.move_to_end(key) return self.cache[key] def put(self, key, value): if key in self.cache: self.cache.move_to_end(key) self.cache[key] = value if len(self.cache) > self.cap: self.cache.popitem(last=False)Output Idle⏱ get/put O(1) · Space O(capacity)Open on LeetCode ↗ -
#45
Merge k Sorted Lists
HardApproach: Min-heap of (val, list-index, node) — pop smallest, push its next.
▼ Show solution ▲ Hide solution
pythonimport heapq def mergeKLists(lists): pq = [] for i, lst in enumerate(lists): if lst: heapq.heappush(pq, (lst.val, i, lst)) dummy = tail = ListNode() while pq: _, i, node = heapq.heappop(pq) tail.next = node tail = node if node.next: heapq.heappush(pq, (node.next.val, i, node.next)) return dummy.nextOutput Idle⏱ Time O(N log k) · Space O(k)Open on LeetCode ↗ -
#46
Invert Binary Tree
EasyApproach: Recursively swap left and right.
▼ Show solution ▲ Hide solution
pythondef invertTree(root): if not root: return None root.left, root.right = invertTree(root.right), invertTree(root.left) return rootOutput Idle⏱ Time O(n) · Space O(h)Open on LeetCode ↗ -
#47
Maximum Depth of Binary Tree
EasyApproach: 1 + max(depth(left), depth(right)).
▼ Show solution ▲ Hide solution
pythondef maxDepth(root): if not root: return 0 return 1 + max(maxDepth(root.left), maxDepth(root.right))Output Idle⏱ Time O(n) · Space O(h)Open on LeetCode ↗ -
#48
Diameter of Binary Tree
EasyApproach: DFS — diameter through a node = leftHeight + rightHeight.
▼ Show solution ▲ Hide solution
pythondef diameterOfBinaryTree(root): best = 0 def depth(n): nonlocal best if not n: return 0 l, r = depth(n.left), depth(n.right) best = max(best, l + r) return 1 + max(l, r) depth(root) return bestOutput Idle⏱ Time O(n) · Space O(h)Open on LeetCode ↗ -
#49
Balanced Binary Tree
EasyApproach: Post-order: return -1 if any subtree is unbalanced.
▼ Show solution ▲ Hide solution
pythondef isBalanced(root): def height(n): if not n: return 0 l = height(n.left) if l == -1: return -1 r = height(n.right) if r == -1 or abs(l - r) > 1: return -1 return 1 + max(l, r) return height(root) != -1Output Idle⏱ Time O(n) · Space O(h)Open on LeetCode ↗ -
#50
Same Tree
EasyApproach: Recurse both trees in lockstep.
▼ Show solution ▲ Hide solution
pythondef isSameTree(p, q): if not p and not q: return True if not p or not q or p.val != q.val: return False return isSameTree(p.left, q.left) and isSameTree(p.right, q.right)Output Idle⏱ Time O(n) · Space O(h)Open on LeetCode ↗ -
#51
Subtree of Another Tree
EasyApproach: At each node of s, check if subtree rooted there equals t.
▼ Show solution ▲ Hide solution
pythondef isSubtree(s, t): if not s: return False return isSameTree(s, t) or isSubtree(s.left, t) or isSubtree(s.right, t)Output Idle⏱ Time O(m·n) · Space O(h)Open on LeetCode ↗ -
#52
Lowest Common Ancestor of a BST
MediumApproach: Walk down — first node where p and q split is the LCA.
▼ Show solution ▲ Hide solution
pythondef lowestCommonAncestor(root, p, q): while root: if p.val < root.val and q.val < root.val: root = root.left elif p.val > root.val and q.val > root.val: root = root.right else: return rootOutput Idle⏱ Time O(h) · Space O(1)Open on LeetCode ↗ -
#53
Binary Tree Level Order Traversal
MediumApproach: BFS, emit one list per level.
▼ Show solution ▲ Hide solution
pythonfrom collections import deque def levelOrder(root): if not root: return [] q, out = deque([root]), [] while q: level = [] for _ in range(len(q)): n = q.popleft() level.append(n.val) if n.left: q.append(n.left) if n.right: q.append(n.right) out.append(level) return outOutput Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#54
Binary Tree Right Side View
MediumApproach: BFS, append the last node of each level.
▼ Show solution ▲ Hide solution
pythonfrom collections import deque def rightSideView(root): if not root: return [] q, out = deque([root]), [] while q: out.append(q[-1].val) for _ in range(len(q)): n = q.popleft() if n.left: q.append(n.left) if n.right: q.append(n.right) return outOutput Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#55
Count Good Nodes in Binary Tree
MediumApproach: DFS carrying the max value seen on the path.
▼ Show solution ▲ Hide solution
pythondef goodNodes(root): def dfs(n, mx): if not n: return 0 good = 1 if n.val >= mx else 0 mx = max(mx, n.val) return good + dfs(n.left, mx) + dfs(n.right, mx) return dfs(root, root.val)Output Idle⏱ Time O(n) · Space O(h)Open on LeetCode ↗ -
#56
Validate Binary Search Tree
MediumApproach: DFS with (lo, hi) bounds for each subtree.
▼ Show solution ▲ Hide solution
pythondef isValidBST(root): def dfs(n, lo, hi): if not n: return True if not (lo < n.val < hi): return False return dfs(n.left, lo, n.val) and dfs(n.right, n.val, hi) return dfs(root, -float('inf'), float('inf'))Output Idle⏱ Time O(n) · Space O(h)Open on LeetCode ↗ -
#57
Kth Smallest Element in a BST
MediumApproach: Iterative in-order traversal, stop at k.
▼ Show solution ▲ Hide solution
pythondef kthSmallest(root, k): stack = [] while root or stack: while root: stack.append(root) root = root.left root = stack.pop() k -= 1 if k == 0: return root.val root = root.rightOutput Idle⏱ Time O(h + k) · Space O(h)Open on LeetCode ↗ -
#58
Construct Binary Tree from Preorder and Inorder
MediumApproach: Use an inorder index map; root is the next preorder value.
▼ Show solution ▲ Hide solution
pythondef buildTree(preorder, inorder): idx = {v: i for i, v in enumerate(inorder)} pre = iter(preorder) def build(l, r): if l > r: return None v = next(pre) node = TreeNode(v) node.left = build(l, idx[v] - 1) node.right = build(idx[v] + 1, r) return node return build(0, len(inorder) - 1)Output Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#59
Binary Tree Maximum Path Sum
HardApproach: DFS — returns max single-arm gain; updates global max with both arms.
▼ Show solution ▲ Hide solution
pythondef maxPathSum(root): best = -float('inf') def dfs(n): nonlocal best if not n: return 0 l = max(dfs(n.left), 0) r = max(dfs(n.right), 0) best = max(best, n.val + l + r) return n.val + max(l, r) dfs(root) return bestOutput Idle⏱ Time O(n) · Space O(h)Open on LeetCode ↗ -
#60
Serialize and Deserialize Binary Tree
HardApproach: Preorder with '#' for None; comma-join for serialize, iter for deserialize.
▼ Show solution ▲ Hide solution
pythonclass Codec: def serialize(self, root): vals = [] def dfs(n): if not n: vals.append('#'); return vals.append(str(n.val)) dfs(n.left); dfs(n.right) dfs(root) return ','.join(vals) def deserialize(self, data): it = iter(data.split(',')) def dfs(): v = next(it) if v == '#': return None n = TreeNode(int(v)) n.left, n.right = dfs(), dfs() return n return dfs()Output Idle⏱ Time O(n) · Space O(n)Open on LeetCode ↗ -
#61
Implement Trie (Prefix Tree)
MediumApproach: Node = dict of children + 'end' flag.
▼ Show solution ▲ Hide solution
pythonclass Trie: def __init__(self): self.children = {} self.end = False def insert(self, w): cur = self for c in w: cur = cur.children.setdefault(c, Trie()) cur.end = True def search(self, w): cur = self._walk(w) return bool(cur) and cur.end def startsWith(self, p): return self._walk(p) is not None def _walk(self, w): cur = self for c in w: if c not in cur.children: return None cur = cur.children[c] return curOutput Idle⏱ insert/search/startsWith O(|w|)Open on LeetCode ↗ -
#62
Design Add and Search Words Data Structure
MediumApproach: Trie + DFS for '.' wildcards.
▼ Show solution ▲ Hide solution
pythonclass WordDictionary: def __init__(self): self.root = {} def addWord(self, w): cur = self.root for c in w: cur = cur.setdefault(c, {}) cur['$'] = True def search(self, w): def dfs(node, i): if i == len(w): return node.get('$', False) c = w[i] if c == '.': return any(dfs(child, i + 1) for k, child in node.items() if k != '$') return c in node and dfs(node[c], i + 1) return dfs(self.root, 0)Output Idle⏱ addWord O(|w|) · search O(26^dots · |w|)Open on LeetCode ↗ -
#63
Word Search II
HardApproach: Trie of all target words + DFS from each cell.
▼ Show solution ▲ Hide solution
pythondef findWords(board, words): root = {} for w in words: cur = root for c in w: cur = cur.setdefault(c, {}) cur['$'] = w rows, cols = len(board), len(board[0]) out = [] def dfs(r, c, node): ch = board[r][c] if ch not in node: return nxt = node[ch] if '$' in nxt: out.append(nxt.pop('$')) board[r][c] = '#' for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)): nr, nc = r + dr, c + dc if 0 <= nr < rows and 0 <= nc < cols: dfs(nr, nc, nxt) board[r][c] = ch for r in range(rows): for c in range(cols): dfs(r, c, root) return outOutput Idle⏱ Time O(M·N·4^L) · Space O(total chars in words)Open on LeetCode ↗ -
#64
Kth Largest Element in a Stream
EasyApproach: Maintain a min-heap of size k.
▼ Show solution ▲ Hide solution
pythonimport heapq class KthLargest: def __init__(self, k, nums): self.k = k self.h = nums heapq.heapify(self.h) while len(self.h) > k: heapq.heappop(self.h) def add(self, v): heapq.heappush(self.h, v) if len(self.h) > self.k: heapq.heappop(self.h) return self.h[0]Output Idle⏱ add O(log k) · Space O(k)Open on LeetCode ↗ -
#65
Last Stone Weight
EasyApproach: Max-heap (negate values); repeatedly smash two largest.
▼ Show solution ▲ Hide solution
pythonimport heapq def lastStoneWeight(stones): h = [-s for s in stones] heapq.heapify(h) while len(h) > 1: a, b = -heapq.heappop(h), -heapq.heappop(h) if a != b: heapq.heappush(h, -(a - b)) return -h[0] if h else 0Output Idle⏱ Time O(n log n) · Space O(n)Open on LeetCode ↗ -
#66
K Closest Points to Origin
MediumApproach: heapq.nsmallest by squared distance.
▼ Show solution ▲ Hide solution
pythonimport heapq def kClosest(points, k): return heapq.nsmallest(k, points, key=lambda p: p[0] ** 2 + p[1] ** 2)Output Idle⏱ Time O(n log k) · Space O(k)Open on LeetCode ↗ -
#67
Kth Largest Element in an Array
MediumApproach: heapq.nlargest(k) → last element, or quickselect.
▼ Show solution ▲ Hide solution
pythonimport heapq def findKthLargest(nums, k): return heapq.nlargest(k, nums)[-1]Output Idle⏱ Time O(n log k) · Space O(k)Open on LeetCode ↗ -
#68
Task Scheduler
MediumApproach: Math — answer = max(len(tasks), (max−1)·(n+1) + tasksWithMaxCount).
▼ Show solution ▲ Hide solution
pythonfrom collections import Counter def leastInterval(tasks, n): cnt = Counter(tasks) mx = max(cnt.values()) most = sum(1 for v in cnt.values() if v == mx) return max(len(tasks), (mx - 1) * (n + 1) + most)Output Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#69
Find Median from Data Stream
HardApproach: Two heaps — max-heap (lower half) and min-heap (upper half) kept balanced.
▼ Show solution ▲ Hide solution
pythonimport heapq class MedianFinder: def __init__(self): self.lo = [] # max-heap (negated) self.hi = [] # min-heap def addNum(self, n): heapq.heappush(self.lo, -n) heapq.heappush(self.hi, -heapq.heappop(self.lo)) if len(self.hi) > len(self.lo): heapq.heappush(self.lo, -heapq.heappop(self.hi)) def findMedian(self): if len(self.lo) > len(self.hi): return -self.lo[0] return (-self.lo[0] + self.hi[0]) / 2Output Idle⏱ add O(log n) · find O(1)Open on LeetCode ↗ -
#70
Subsets
MediumApproach: For each number, double the result list (include / exclude).
▼ Show solution ▲ Hide solution
pythondef subsets(nums): out = [[]] for n in nums: out += [s + [n] for s in out] return outOutput Idle⏱ Time O(n·2ⁿ) · Space O(n·2ⁿ)Open on LeetCode ↗ -
#71
Combination Sum
MediumApproach: Backtrack — at each step, reuse candidate or move on.
▼ Show solution ▲ Hide solution
pythondef combinationSum(candidates, target): out = [] def bt(start, remain, path): if remain == 0: out.append(path[:]); return for i in range(start, len(candidates)): c = candidates[i] if c <= remain: path.append(c) bt(i, remain - c, path) path.pop() bt(0, target, []) return outOutput Idle⏱ Time exponential (depends on target/min(candidates))Open on LeetCode ↗ -
#72
Permutations
MediumApproach: Backtrack with a 'used' boolean array.
▼ Show solution ▲ Hide solution
pythondef permute(nums): out = [] def bt(path, used): if len(path) == len(nums): out.append(path[:]); return for i, n in enumerate(nums): if not used[i]: used[i] = True path.append(n) bt(path, used) path.pop() used[i] = False bt([], [False] * len(nums)) return outOutput Idle⏱ Time O(n·n!) · Space O(n)Open on LeetCode ↗ -
#73
Word Search
MediumApproach: DFS from each cell, marking visited via in-place edit.
▼ Show solution ▲ Hide solution
pythondef exist(board, word): rows, cols = len(board), len(board[0]) def dfs(r, c, i): if i == len(word): return True if r < 0 or c < 0 or r >= rows or c >= cols or board[r][c] != word[i]: return False tmp, board[r][c] = board[r][c], '#' ok = any(dfs(r + dr, c + dc, i + 1) for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1))) board[r][c] = tmp return ok return any(dfs(r, c, 0) for r in range(rows) for c in range(cols))Output Idle⏱ Time O(M·N·4^L) · Space O(L)Open on LeetCode ↗ -
#74
N-Queens
HardApproach: Backtrack by row; track used columns and both diagonals via sets.
▼ Show solution ▲ Hide solution
pythondef solveNQueens(n): out = [] cols, d1, d2 = set(), set(), set() board = [['.'] * n for _ in range(n)] def bt(r): if r == n: out.append([''.join(row) for row in board]); return for c in range(n): if c in cols or r + c in d1 or r - c in d2: continue cols.add(c); d1.add(r + c); d2.add(r - c) board[r][c] = 'Q' bt(r + 1) board[r][c] = '.' cols.remove(c); d1.remove(r + c); d2.remove(r - c) bt(0) return outOutput Idle⏱ Time O(n!) · Space O(n)Open on LeetCode ↗ -
#75
Number of Islands
MediumApproach: DFS / BFS from each unvisited '1', marking the island.
▼ Show solution ▲ Hide solution
pythondef numIslands(grid): if not grid: return 0 rows, cols = len(grid), len(grid[0]) def dfs(r, c): if r < 0 or c < 0 or r >= rows or c >= cols or grid[r][c] != '1': return grid[r][c] = '0' for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)): dfs(r + dr, c + dc) count = 0 for r in range(rows): for c in range(cols): if grid[r][c] == '1': dfs(r, c) count += 1 return countOutput Idle⏱ Time O(M·N) · Space O(M·N)Open on LeetCode ↗ -
#76
Max Area of Island
MediumApproach: DFS returning size of each island.
▼ Show solution ▲ Hide solution
pythondef maxAreaOfIsland(grid): rows, cols = len(grid), len(grid[0]) def dfs(r, c): if r < 0 or c < 0 or r >= rows or c >= cols or grid[r][c] != 1: return 0 grid[r][c] = 0 return 1 + dfs(r + 1, c) + dfs(r - 1, c) + dfs(r, c + 1) + dfs(r, c - 1) return max((dfs(r, c) for r in range(rows) for c in range(cols)), default=0)Output Idle⏱ Time O(M·N) · Space O(M·N)Open on LeetCode ↗ -
#77
Clone Graph
MediumApproach: DFS with old→new map.
▼ Show solution ▲ Hide solution
pythondef cloneGraph(node): if not node: return None mp = {} def dfs(n): if n in mp: return mp[n] clone = Node(n.val) mp[n] = clone for nb in n.neighbors: clone.neighbors.append(dfs(nb)) return clone return dfs(node)Output Idle⏱ Time O(V + E) · Space O(V)Open on LeetCode ↗ -
#78
Walls and Gates
MediumApproach: Multi-source BFS from every gate.
▼ Show solution ▲ Hide solution
pythonfrom collections import deque INF = 2147483647 def wallsAndGates(rooms): rows, cols = len(rooms), len(rooms[0]) q = deque() for r in range(rows): for c in range(cols): if rooms[r][c] == 0: q.append((r, c)) while q: r, c = q.popleft() for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)): nr, nc = r + dr, c + dc if 0 <= nr < rows and 0 <= nc < cols and rooms[nr][nc] == INF: rooms[nr][nc] = rooms[r][c] + 1 q.append((nr, nc))Output Idle⏱ Time O(M·N) · Space O(M·N)Open on LeetCode ↗ -
#79
Rotting Oranges
MediumApproach: Multi-source BFS from rotten oranges; track time and fresh count.
▼ Show solution ▲ Hide solution
pythonfrom collections import deque def orangesRotting(grid): rows, cols = len(grid), len(grid[0]) q = deque(); fresh = 0 for r in range(rows): for c in range(cols): if grid[r][c] == 2: q.append((r, c, 0)) elif grid[r][c] == 1: fresh += 1 minutes = 0 while q: r, c, t = q.popleft() minutes = t for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)): nr, nc = r + dr, c + dc if 0 <= nr < rows and 0 <= nc < cols and grid[nr][nc] == 1: grid[nr][nc] = 2; fresh -= 1 q.append((nr, nc, t + 1)) return minutes if fresh == 0 else -1Output Idle⏱ Time O(M·N) · Space O(M·N)Open on LeetCode ↗ -
#80
Pacific Atlantic Water Flow
MediumApproach: Two multi-source DFS from each ocean; answer = intersection of reachable cells.
▼ Show solution ▲ Hide solution
pythondef pacificAtlantic(h): rows, cols = len(h), len(h[0]) pac, atl = set(), set() def dfs(r, c, visited, prev): if (r, c) in visited or r < 0 or c < 0 or r >= rows or c >= cols or h[r][c] < prev: return visited.add((r, c)) for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)): dfs(r + dr, c + dc, visited, h[r][c]) for c in range(cols): dfs(0, c, pac, h[0][c]) dfs(rows - 1, c, atl, h[rows - 1][c]) for r in range(rows): dfs(r, 0, pac, h[r][0]) dfs(r, cols - 1, atl, h[r][cols - 1]) return list(pac & atl)Output Idle⏱ Time O(M·N) · Space O(M·N)Open on LeetCode ↗ -
#81
Surrounded Regions
MediumApproach: Mark border-connected 'O's as safe, flip remaining 'O' to 'X'.
▼ Show solution ▲ Hide solution
pythondef solve(board): if not board: return rows, cols = len(board), len(board[0]) def dfs(r, c): if r < 0 or c < 0 or r >= rows or c >= cols or board[r][c] != 'O': return board[r][c] = 'S' for dr, dc in ((1, 0), (-1, 0), (0, 1), (0, -1)): dfs(r + dr, c + dc) for r in range(rows): dfs(r, 0); dfs(r, cols - 1) for c in range(cols): dfs(0, c); dfs(rows - 1, c) for r in range(rows): for c in range(cols): board[r][c] = 'O' if board[r][c] == 'S' else 'X'Output Idle⏱ Time O(M·N) · Space O(M·N)Open on LeetCode ↗ -
#82
Course Schedule
MediumApproach: Topological sort (Kahn's). Possible iff all courses can be queued.
▼ Show solution ▲ Hide solution
pythonfrom collections import defaultdict, deque def canFinish(n, prerequisites): graph = defaultdict(list) indeg = [0] * n for a, b in prerequisites: graph[b].append(a); indeg[a] += 1 q = deque(i for i in range(n) if indeg[i] == 0) done = 0 while q: c = q.popleft(); done += 1 for nxt in graph[c]: indeg[nxt] -= 1 if indeg[nxt] == 0: q.append(nxt) return done == nOutput Idle⏱ Time O(V + E) · Space O(V + E)Open on LeetCode ↗ -
#83
Course Schedule II
MediumApproach: Same Kahn's topo sort, output the order taken.
▼ Show solution ▲ Hide solution
pythonfrom collections import defaultdict, deque def findOrder(n, prerequisites): graph = defaultdict(list) indeg = [0] * n for a, b in prerequisites: graph[b].append(a); indeg[a] += 1 q = deque(i for i in range(n) if indeg[i] == 0) order = [] while q: c = q.popleft(); order.append(c) for nxt in graph[c]: indeg[nxt] -= 1 if indeg[nxt] == 0: q.append(nxt) return order if len(order) == n else []Output Idle⏱ Time O(V + E) · Space O(V + E)Open on LeetCode ↗ -
#84
Redundant Connection
MediumApproach: Union-Find — first edge whose endpoints already share a root is the answer.
▼ Show solution ▲ Hide solution
pythondef findRedundantConnection(edges): parent = list(range(len(edges) + 1)) def find(x): while parent[x] != x: parent[x] = parent[parent[x]] x = parent[x] return x for u, v in edges: ru, rv = find(u), find(v) if ru == rv: return [u, v] parent[ru] = rvOutput Idle⏱ Time O(n·α(n)) ≈ O(n) · Space O(n)Open on LeetCode ↗ -
#85
Number of Connected Components in an Undirected Graph
MediumApproach: Union-Find — count = n − successful unions.
▼ Show solution ▲ Hide solution
pythondef countComponents(n, edges): parent = list(range(n)) def find(x): while parent[x] != x: parent[x] = parent[parent[x]] x = parent[x] return x comp = n for u, v in edges: ru, rv = find(u), find(v) if ru != rv: parent[ru] = rv comp -= 1 return compOutput Idle⏱ Time O((n + e)·α(n)) · Space O(n)Open on LeetCode ↗ -
#86
Word Ladder
HardApproach: BFS over a pattern graph: each '*'-masked word indexes its 1-letter neighbors.
▼ Show solution ▲ Hide solution
pythonfrom collections import defaultdict, deque def ladderLength(begin, end, wordList): if end not in wordList: return 0 L = len(begin) pat = defaultdict(list) for w in wordList: for i in range(L): pat[w[:i] + '*' + w[i + 1:]].append(w) q = deque([(begin, 1)]); seen = {begin} while q: w, d = q.popleft() for i in range(L): key = w[:i] + '*' + w[i + 1:] for nb in pat[key]: if nb == end: return d + 1 if nb not in seen: seen.add(nb) q.append((nb, d + 1)) return 0Output Idle⏱ Time O(N·L²) · Space O(N·L²)Open on LeetCode ↗ -
#87
Climbing Stairs
EasyApproach: Fibonacci-style DP — ways(n) = ways(n−1) + ways(n−2).
▼ Show solution ▲ Hide solution
pythondef climbStairs(n): a, b = 1, 1 for _ in range(n): a, b = b, a + b return aOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#88
Min Cost Climbing Stairs
EasyApproach: dp[i] = min(dp[i−1] + cost[i−1], dp[i−2] + cost[i−2]).
▼ Show solution ▲ Hide solution
pythondef minCostClimbingStairs(cost): a, b = 0, 0 for i in range(2, len(cost) + 1): a, b = b, min(b + cost[i - 1], a + cost[i - 2]) return bOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#89
House Robber
MediumApproach: dp[i] = max(dp[i−1], dp[i−2] + nums[i]). Only need two rolling values.
▼ Show solution ▲ Hide solution
pythondef rob(nums): a, b = 0, 0 for n in nums: a, b = b, max(b, a + n) return bOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#90
House Robber II
MediumApproach: Run House Robber on two slices: skip first house, skip last house.
▼ Show solution ▲ Hide solution
pythondef rob(nums): if len(nums) == 1: return nums[0] def line(arr): a, b = 0, 0 for n in arr: a, b = b, max(b, a + n) return b return max(line(nums[1:]), line(nums[:-1]))Output Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#91
Longest Palindromic Substring
MediumApproach: Expand around each center (odd and even length).
▼ Show solution ▲ Hide solution
pythondef longestPalindrome(s): best = '' for i in range(len(s)): for l, r in ((i, i), (i, i + 1)): while l >= 0 and r < len(s) and s[l] == s[r]: if r - l + 1 > len(best): best = s[l:r + 1] l -= 1; r += 1 return bestOutput Idle⏱ Time O(n²) · Space O(1)Open on LeetCode ↗ -
#92
Decode Ways
MediumApproach: dp[i] = dp[i−1] (1-digit) + dp[i−2] (2-digit) if valid.
▼ Show solution ▲ Hide solution
pythondef numDecodings(s): if not s or s[0] == '0': return 0 a, b = 1, 1 for i in range(1, len(s)): cur = 0 if s[i] != '0': cur += b two = int(s[i - 1:i + 1]) if 10 <= two <= 26: cur += a a, b = b, cur return bOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#93
Coin Change
MediumApproach: Unbounded knapsack DP — dp[a] = min(dp[a], dp[a−c] + 1).
▼ Show solution ▲ Hide solution
pythondef coinChange(coins, amount): dp = [amount + 1] * (amount + 1) dp[0] = 0 for a in range(1, amount + 1): for c in coins: if c <= a: dp[a] = min(dp[a], dp[a - c] + 1) return dp[amount] if dp[amount] <= amount else -1Output Idle⏱ Time O(amount · |coins|) · Space O(amount)Open on LeetCode ↗ -
#94
Maximum Product Subarray
MediumApproach: Track current max AND min (negatives can flip).
▼ Show solution ▲ Hide solution
pythondef maxProduct(nums): res = lo = hi = nums[0] for n in nums[1:]: a, b = n * lo, n * hi lo, hi = min(n, a, b), max(n, a, b) res = max(res, hi) return resOutput Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗ -
#95
Word Break
MediumApproach: dp[i] true if some j < i has dp[j] true and s[j:i] is in dict.
▼ Show solution ▲ Hide solution
pythondef wordBreak(s, wordDict): words = set(wordDict) dp = [False] * (len(s) + 1) dp[0] = True for i in range(1, len(s) + 1): for j in range(i): if dp[j] and s[j:i] in words: dp[i] = True break return dp[-1]Output Idle⏱ Time O(n²) · Space O(n)Open on LeetCode ↗ -
#96
Longest Increasing Subsequence
MediumApproach: Patience sort: maintain 'tails' and binary-search each number's position.
▼ Show solution ▲ Hide solution
pythonimport bisect def lengthOfLIS(nums): tails = [] for n in nums: i = bisect.bisect_left(tails, n) if i == len(tails): tails.append(n) else: tails[i] = n return len(tails)Output Idle⏱ Time O(n log n) · Space O(n)Open on LeetCode ↗ -
#97
Unique Paths
MediumApproach: Each cell = left + above; rolling 1D array suffices.
▼ Show solution ▲ Hide solution
pythondef uniquePaths(m, n): row = [1] * n for _ in range(1, m): for c in range(1, n): row[c] += row[c - 1] return row[-1]Output Idle⏱ Time O(m·n) · Space O(n)Open on LeetCode ↗ -
#98
Longest Common Subsequence
MediumApproach: Classic 2D DP table — match adds 1, mismatch takes max(top, left).
▼ Show solution ▲ Hide solution
pythondef longestCommonSubsequence(a, b): m, n = len(a), len(b) dp = [[0] * (n + 1) for _ in range(m + 1)] for i in range(1, m + 1): for j in range(1, n + 1): if a[i - 1] == b[j - 1]: dp[i][j] = dp[i - 1][j - 1] + 1 else: dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) return dp[m][n]Output Idle⏱ Time O(m·n) · Space O(m·n)Open on LeetCode ↗ -
#99
Edit Distance
HardApproach: 2D DP — match → diag; else 1 + min(insert, delete, replace).
▼ Show solution ▲ Hide solution
pythondef minDistance(a, b): m, n = len(a), len(b) dp = [[0] * (n + 1) for _ in range(m + 1)] for i in range(m + 1): dp[i][0] = i for j in range(n + 1): dp[0][j] = j for i in range(1, m + 1): for j in range(1, n + 1): if a[i - 1] == b[j - 1]: dp[i][j] = dp[i - 1][j - 1] else: dp[i][j] = 1 + min(dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]) return dp[m][n]Output Idle⏱ Time O(m·n) · Space O(m·n)Open on LeetCode ↗ -
#100
Best Time to Buy and Sell Stock with Cooldown
MediumApproach: 3-state machine: hold / sold / rest — transitions each day.
▼ Show solution ▲ Hide solution
pythondef maxProfit(prices): hold, sold, rest = -float('inf'), 0, 0 for p in prices: prev_hold = hold hold = max(hold, rest - p) rest = max(rest, sold) sold = prev_hold + p return max(sold, rest)Output Idle⏱ Time O(n) · Space O(1)Open on LeetCode ↗
No coding questions match those filters. Try clearing the search or changing difficulty.
-
Q1
What is the difference between a list and a tuple?
Easy▼ Show answer ▲ Hide answer
Lists are mutable (can grow, shrink, and have elements reassigned) and use square brackets []. Tuples are immutable (fixed once created) and use parentheses (). Because tuples are immutable, they are hashable, so they can be used as dict keys or set members — lists cannot. Tuples are slightly faster and use less memory; lists offer many more methods (append, pop, sort, etc.).
pythonlst = [1, 2, 3]; lst.append(4) # OK tup = (1, 2, 3); tup[0] = 9 # TypeError d = {(1, 2): 'point'} # OK — tuple is hashableOutput Idle -
Q2
What are mutable and immutable types in Python?
Easy▼ Show answer ▲ Hide answer
Mutable objects can be changed in place after creation: list, dict, set, bytearray, and most user-defined classes. Immutable objects cannot be modified: int, float, bool, str, bytes, tuple, frozenset, None. Only immutable objects (or objects that hash by identity) can be used as dict keys or stored in a set.
-
Q3
What is the difference between `is` and `==`?
Easy▼ Show answer ▲ Hide answer
`==` compares values (calls `__eq__`); `is` checks identity — whether both names point to the same object in memory (same `id()`). Use `is` for singletons like `None`, `True`, `False`. Small integers (-5 to 256) and short strings may be interned, so `is` can accidentally appear to work on them — never rely on it for value comparison.
pythona = [1, 2]; b = [1, 2] a == b # True (same value) a is b # False (different objects) x = None x is None # always use this, not x == NoneOutput Idle -
Q4
What is `None` and how is it different from `null`?
Easy▼ Show answer ▲ Hide answer
`None` is Python's singleton object representing the absence of a value, of type `NoneType`. Unlike `null` in some languages, there is exactly one `None` instance for the entire program, so comparison via `is None` is the idiomatic and fastest check. Functions that don't explicitly return anything implicitly return `None`.
-
Q5
How does Python handle integer overflow? What's the max int?
Easy▼ Show answer ▲ Hide answer
Python 3 integers are arbitrary-precision — they grow as needed, limited only by available memory. There is no `INT_MAX`. `sys.maxsize` reports the largest size a container can hold on the platform (`2**63 - 1` on 64-bit), but plain `int` arithmetic can exceed it freely.
pythonimport sys sys.maxsize # 9223372036854775807 on 64-bit 2 ** 1000 # works fine — no overflowOutput Idle -
Q6
What is the difference between shallow `copy` and `deepcopy`?
Medium▼ Show answer ▲ Hide answer
`copy.copy(x)` creates a new outer object but its elements still reference the same inner objects — mutating a nested object affects both copies. `copy.deepcopy(x)` recursively copies every nested object, so the two are fully independent. Assignment (`y = x`) creates no copy at all — just another name for the same object.
pythonimport copy a = [[1, 2], [3, 4]] b = copy.copy(a); b[0].append(9) # also mutates a[0] c = copy.deepcopy(a); c[0].append(9) # a untouchedOutput Idle -
Q7
What are f-strings and why prefer them?
Easy▼ Show answer ▲ Hide answer
f-strings (PEP 498, Python 3.6+) embed expressions inside string literals using `f"...{expr}..."`. They are faster than `%` formatting and `str.format()`, more readable, and support format-spec, `=` for debug-style output (3.8+), and inline method calls.
pythonname, n = 'Ada', 7 f'{name} ran {n*2} miles' # 'Ada ran 14 miles' f'{n=}, {n*2=}' # 'n=7, n*2=14' f'{3.14159:.2f}' # '3.14'Output Idle -
Q8
What is the difference between `range` and a list of numbers?
Easy▼ Show answer ▲ Hide answer
`range(n)` is a lazy, immutable sequence object — it computes values on demand and uses constant memory regardless of size. A list of numbers stores every value, using O(n) memory. In Python 3, `range` replaced Python 2's `xrange`; there is no `xrange` anymore.
pythonsys.getsizeof(range(10**9)) # 48 bytes # list(range(10**9)) # would OOM the machineOutput Idle -
Q9
What is the difference between `__init__`, `__new__`, and `__call__`?
Medium▼ Show answer ▲ Hide answer
`__new__(cls, ...)` is the actual constructor — it allocates and returns a new instance. `__init__(self, ...)` is the initializer — it configures the already-created instance and returns `None`. `__call__(self, ...)` makes an instance callable like a function (e.g., `obj()`). You rarely override `__new__` outside of metaclasses, immutable subclasses, or singletons.
pythonclass Adder: def __init__(self, x): self.x = x def __call__(self, y): return self.x + y add5 = Adder(5) add5(3) # 8 — instance used like a functionOutput Idle -
Q10
`@classmethod` vs `@staticmethod` vs instance method — when to use each?
Medium▼ Show answer ▲ Hide answer
Instance methods take `self` and operate on a specific instance. `@classmethod` takes `cls` and operates on the class itself — used for alternative constructors (`from_string`, `from_dict`) and factories. `@staticmethod` takes neither — it's a regular function living in the class namespace for grouping; use it when the logic is related to the class but doesn't need access to instance or class state.
pythonclass Date: def __init__(self, y, m, d): self.y, self.m, self.d = y, m, d @classmethod def from_iso(cls, s): return cls(*map(int, s.split('-'))) @staticmethod def is_leap(y): return y % 4 == 0 and (y % 100 != 0 or y % 400 == 0)Output Idle -
Q11
What is Method Resolution Order (MRO) and the C3 linearization?
Hard▼ Show answer ▲ Hide answer
MRO is the order Python searches base classes when looking up an attribute on a class that has multiple parents. Python 3 uses the C3 linearization algorithm, which guarantees that each class appears before its parents and that the order in the bases list is preserved. Inspect via `Cls.__mro__` or `Cls.mro()`. C3 also detects inconsistent hierarchies at class-creation time and raises `TypeError`.
pythonclass A: pass class B(A): pass class C(A): pass class D(B, C): pass D.__mro__ # (D, B, C, A, object)Output Idle -
Q12
What are dunder/magic methods? Name five important ones.
Medium▼ Show answer ▲ Hide answer
Dunder (double-underscore) methods let your class integrate with Python's syntax and built-ins. Important ones: `__init__` (initialize), `__repr__` (debug string), `__str__` (user string), `__len__` (len()), `__iter__` (for-loop), `__eq__`/`__hash__` (equality and hashing), `__getitem__`/`__setitem__` (subscript), `__enter__`/`__exit__` (context manager), `__call__` (callable instances).
-
Q13
What is the difference between `__str__` and `__repr__`?
Easy▼ Show answer ▲ Hide answer
`__repr__` is for developers — should be unambiguous and ideally `eval()`-able to recreate the object; shown in the REPL and by `repr(x)`. `__str__` is for end users — readable and friendly; called by `print(x)` and `str(x)`. If only `__repr__` is defined, `str()` falls back to it.
pythonclass Point: def __init__(self, x, y): self.x, self.y = x, y def __repr__(self): return f'Point({self.x}, {self.y})' def __str__(self): return f'({self.x}, {self.y})'Output Idle -
Q14
What is `super()` and why use it?
Medium▼ Show answer ▲ Hide answer
`super()` returns a proxy that delegates attribute lookups to the next class in the MRO — not necessarily the literal parent. Using `super().__init__(...)` instead of `Parent.__init__(self, ...)` makes cooperative multiple inheritance work correctly and lets you change the base class without rewriting calls. In Python 3 you can call `super()` with no arguments inside a method.
-
Q15
What are Abstract Base Classes (ABCs)?
Medium▼ Show answer ▲ Hide answer
ABCs (`abc.ABC`, `@abc.abstractmethod`) define an interface that subclasses must implement; instantiating a class with unimplemented abstract methods raises `TypeError`. Python's `collections.abc` module provides reusable ABCs (`Iterable`, `Mapping`, `Sequence`, etc.). They enable `isinstance()` checks against an interface rather than a concrete class.
pythonfrom abc import ABC, abstractmethod class Storage(ABC): @abstractmethod def save(self, key, value): ... # Storage() -> TypeError: abstractOutput Idle -
Q16
Composition vs inheritance — when to prefer which?
Medium▼ Show answer ▲ Hide answer
Inheritance models 'is-a' relationships and shares implementation through subclassing — but couples the child to the parent's internals and creates fragile hierarchies. Composition models 'has-a' relationships by holding instances of other classes — more flexible and easier to test. The widely-followed rule is 'favor composition over inheritance' — use inheritance only when subtypes truly substitute for the base type (Liskov Substitution).
-
Q17
What is a decorator and how does it work?
Medium▼ Show answer ▲ Hide answer
A decorator is a callable that takes a function (or class) and returns a replacement. `@dec` above a definition is sugar for `f = dec(f)`. Decorators are used for cross-cutting concerns: logging, caching, timing, access control, registration. Use `functools.wraps` inside the wrapper to preserve the original `__name__`, `__doc__`, and signature.
pythonfrom functools import wraps def log(fn): @wraps(fn) def wrapper(*a, **kw): print(f'calling {fn.__name__}') return fn(*a, **kw) return wrapper @log def add(a, b): return a + bOutput Idle -
Q18
What is a closure?
Medium▼ Show answer ▲ Hide answer
A closure is an inner function that remembers (captures) variables from its enclosing scope even after the outer function has returned. The captured variables are stored in the function's `__closure__` cells. To modify a captured variable from the inner function, use the `nonlocal` keyword.
pythondef make_counter(): count = 0 def inc(): nonlocal count count += 1 return count return inc c = make_counter(); c(); c() # 1, 2Output Idle -
Q19
Explain `*args` and `**kwargs`.
Easy▼ Show answer ▲ Hide answer
`*args` collects extra positional arguments into a tuple; `**kwargs` collects extra keyword arguments into a dict. On the calling side, `*` and `**` unpack iterables and mappings into arguments. The names `args`/`kwargs` are convention — only the `*` and `**` are syntactically required.
pythondef trace(*args, **kwargs): print(args, kwargs) opts = {'verbose': True} trace(1, 2, name='ada', **opts) # (1, 2) {'name': 'ada', 'verbose': True}Output Idle -
Q20
What is a lambda and when should you avoid one?
Easy▼ Show answer ▲ Hide answer
A `lambda` is an anonymous, single-expression function. It's handy as a short callback to `sorted`, `map`, `filter`, or `key=...` arguments. Avoid lambdas when: the logic spans multiple statements, you need type hints or a docstring, or naming the function would make the code clearer — PEP 8 specifically discourages assigning a lambda to a name (use `def` instead).
-
Q21
What does `functools.lru_cache` do?
Medium▼ Show answer ▲ Hide answer
`@lru_cache(maxsize=N)` memoizes a function's results in a dict keyed by its arguments, evicting least-recently-used entries when full. Arguments must be hashable. It's a one-line speedup for pure functions with repeated calls — recursive ones especially. Use `maxsize=None` for an unbounded cache, or `@functools.cache` (3.9+) as a shortcut.
pythonfrom functools import lru_cache @lru_cache(maxsize=None) def fib(n): return n if n < 2 else fib(n - 1) + fib(n - 2) fib(100) # instantOutput Idle -
Q22
What does `@property` do?
Medium▼ Show answer ▲ Hide answer
`@property` turns a method into a read-only attribute accessed without parentheses. Pair it with `@<name>.setter` / `@<name>.deleter` to add write/delete behavior. Useful for computed attributes and for adding validation while keeping a clean attribute-style API.
pythonclass Circle: def __init__(self, r): self._r = r @property def area(self): return 3.14159 * self._r ** 2 @property def r(self): return self._r @r.setter def r(self, v): if v < 0: raise ValueError self._r = vOutput Idle -
Q23
What does `functools.wraps` do and why is it important?
Easy▼ Show answer ▲ Hide answer
When you wrap a function inside a decorator, the wrapper replaces the original's `__name__`, `__doc__`, `__module__`, `__qualname__`, and signature. `@functools.wraps(original)` on the wrapper copies that metadata back, which keeps debuggers, `help()`, stack traces, and tools like Sphinx working correctly.
-
Q24
What is the difference between an iterable and an iterator?
Medium▼ Show answer ▲ Hide answer
An iterable is any object with `__iter__()` that returns an iterator — lists, tuples, strings, dicts, sets, files. An iterator is an object with both `__iter__()` (returns itself) and `__next__()` (returns the next value or raises `StopIteration`). Iterators are single-use; iterables can be iterated repeatedly by producing a fresh iterator each time.
pythonnums = [1, 2, 3] it = iter(nums) next(it), next(it) # 1, 2 for n in nums: pass # works — list is iterable for n in it: pass # only yields 3 — iterator is consumedOutput Idle -
Q25
What is a generator and how is it different from a regular function?
Medium▼ Show answer ▲ Hide answer
A generator is a function that contains `yield`; calling it returns a generator object (an iterator) rather than running the body. Each `next()` advances to the next `yield` and pauses, preserving local state. Generators produce values lazily — ideal for large or infinite sequences — and use O(1) memory regardless of length.
pythondef squares(n): for i in range(n): yield i * i list(squares(5)) # [0, 1, 4, 9, 16]Output Idle -
Q26
What is the difference between `yield` and `return`?
Easy▼ Show answer ▲ Hide answer
`return` exits a function and produces a single value. `yield` produces a value but pauses the function — the next call resumes right after the `yield`, with all locals intact. A function with any `yield` becomes a generator function; a bare `return` (or running off the end) raises `StopIteration` from a generator.
-
Q27
What does `yield from` do?
Medium▼ Show answer ▲ Hide answer
`yield from iterable` delegates iteration to another iterable or sub-generator — it yields every value the inner iterable produces and forwards `send()`, `throw()`, and the return value back to the caller. It's the idiomatic way to chain generators without writing a manual `for` loop.
pythondef chain(*iters): for it in iters: yield from it list(chain([1, 2], (3, 4))) # [1, 2, 3, 4]Output Idle -
Q28
How do you create an infinite iterator?
Easy▼ Show answer ▲ Hide answer
Use a generator with a `while True` loop and `yield`, or one of `itertools.count`, `itertools.cycle`, or `itertools.repeat`. Always combine with a finite construct like `itertools.islice` or `break` so the program terminates.
pythonfrom itertools import count, islice list(islice(count(10, 2), 5)) # [10, 12, 14, 16, 18]Output Idle -
Q29
What is the GIL and why does Python have it?
Hard▼ Show answer ▲ Hide answer
The Global Interpreter Lock is a CPython mutex that allows only one thread to execute Python bytecode at a time. It exists because CPython's memory management (reference counting) isn't thread-safe — the GIL simplifies the C API and the interpreter implementation. The consequence: pure-Python CPU-bound code does not benefit from threading. Use `multiprocessing`, `concurrent.futures.ProcessPoolExecutor`, native extensions (NumPy, etc.) that release the GIL, or asyncio for I/O-bound work. Python 3.13 introduced an experimental free-threaded build (PEP 703) that can run without the GIL.
-
Q30
How does Python's garbage collection work?
Hard▼ Show answer ▲ Hide answer
CPython uses two mechanisms together. (1) Reference counting: every object tracks how many references point to it; when the count drops to zero, it's immediately freed. (2) A generational cyclic GC (the `gc` module) periodically scans for reference cycles that pure refcounting cannot collect (e.g., a list containing itself). Objects survive across three generations, scanned with decreasing frequency. You can disable, tune, or force collection with `gc.disable()`, `gc.set_threshold()`, `gc.collect()`.
-
Q31
What is reference counting?
Medium▼ Show answer ▲ Hide answer
Every CPython object holds a counter of how many references point to it. Assigning the object to a new name, passing it to a function, or putting it in a container increments the count; rebinding, returning, or `del` decrements it. When the count reaches zero, the object is freed immediately. `sys.getrefcount(x)` exposes it (returns one more than the actual count because the argument itself is a reference).
-
Q32
When should you use `__slots__`?
Hard▼ Show answer ▲ Hide answer
Defining `__slots__ = ('a', 'b')` tells Python to store attributes in a fixed array instead of a per-instance `__dict__`. This saves significant memory (often 40–50%) and speeds attribute access for classes you instantiate millions of times. Trade-offs: you can no longer add new attributes at runtime, multiple inheritance with non-slotted parents gets tricky, and `__weakref__` must be listed explicitly if needed.
pythonclass Point: __slots__ = ('x', 'y') def __init__(self, x, y): self.x, self.y = x, yOutput Idle -
Q33
Threading vs multiprocessing — when to use each?
Medium▼ Show answer ▲ Hide answer
Threading runs multiple threads in one process, sharing memory — but the GIL serializes Python bytecode, so it only helps for I/O-bound work (network, disk) where threads release the GIL while waiting. Multiprocessing spawns separate OS processes, each with its own GIL — true parallelism for CPU-bound work, at the cost of higher startup overhead and serialization between processes. For high-concurrency I/O, `asyncio` is usually a better fit than threads.
-
Q34
Why is Python slower than C? How do you make it faster?
Medium▼ Show answer ▲ Hide answer
Python is interpreted, dynamically typed, and every operation goes through layers of object dispatch and refcount bookkeeping. To speed it up: profile first (`cProfile`), pick better algorithms/data structures, use built-ins and the standard library (they're written in C), use NumPy / Pandas for numerical work, cache with `functools.lru_cache`, JIT-compile hot loops with Numba or PyPy, or write C extensions with Cython / pybind11. Premature optimization is the usual mistake.
-
Q35
Explain `try`, `except`, `else`, `finally`.
Easy▼ Show answer ▲ Hide answer
`try` wraps risky code. `except` catches matching exceptions (be specific, not bare). `else` runs only if `try` succeeded with no exception — use it to scope the 'success path' tightly so `except` doesn't accidentally catch unrelated errors. `finally` always runs (success, exception, or `return`) — use for cleanup like closing handles.
pythontry: f = open('x.txt') except FileNotFoundError: data = '' else: data = f.read() finally: if 'f' in dir(): f.close()Output Idle -
Q36
What is the difference between an exception and a syntax error?
Easy▼ Show answer ▲ Hide answer
A `SyntaxError` is raised by the parser before any code runs — your source isn't valid Python, so nothing executes. Exceptions occur at runtime while valid code is running (`ZeroDivisionError`, `KeyError`, etc.) and can be caught with `try/except`. `SyntaxError` *technically* inherits from `Exception` but you generally cannot catch the parse-time form.
-
Q37
How do you create a custom exception?
Easy▼ Show answer ▲ Hide answer
Subclass `Exception` (or a more specific built-in like `ValueError`). Keep the hierarchy shallow and meaningful — usually one base exception per package and subclasses for distinct failure modes. Override `__init__` only if you need extra context attributes.
pythonclass PaymentError(Exception): pass class InsufficientFunds(PaymentError): def __init__(self, needed, have): super().__init__(f'Need {needed}, have {have}') self.needed, self.have = needed, haveOutput Idle -
Q38
What is the purpose of `__init__.py`?
Easy▼ Show answer ▲ Hide answer
`__init__.py` marks a directory as a regular Python package. It runs when the package is first imported, so it's the place to expose a curated public API (re-export names, define `__all__`, set `__version__`). Since Python 3.3, directories without `__init__.py` work as 'namespace packages', but most projects still ship one for clarity and to control imports.
-
Q39
`import x` vs `from x import y` — what's the difference?
Easy▼ Show answer ▲ Hide answer
`import x` binds the module object to the name `x`; access its members as `x.y`. `from x import y` binds only `y` in the current namespace — shorter to use, but loses the module context and risks shadowing. `from x import *` should be avoided in application code; it pollutes the namespace and obscures origins. Both run the module exactly once — subsequent imports return the cached module from `sys.modules`.
-
Q40
What are virtual environments and why use them?
Easy▼ Show answer ▲ Hide answer
A virtual environment is an isolated Python installation with its own `site-packages`, so each project can pin its own dependency versions without conflicting with others or the system Python. Create one with `python -m venv .venv`, activate it, then `pip install`. Modern tools like `uv`, `poetry`, and `pipenv` automate this further with lockfiles.
pythonpython -m venv .venv .venv\Scripts\activate # Windows source .venv/bin/activate # macOS/Linux pip install -r requirements.txtOutput Idle -
Q41
What is `asyncio` and when should you use it?
Medium▼ Show answer ▲ Hide answer
`asyncio` is Python's standard library for cooperative concurrency using coroutines on a single thread. The event loop schedules thousands of awaitable tasks that pause at `await` points and yield to others while waiting for I/O. Use it for I/O-bound workloads with high concurrency (HTTP clients, sockets, database drivers that support async). It does NOT help CPU-bound code — the work still runs on one thread.
-
Q42
What is the difference between `async def` and a regular `def`?
Medium▼ Show answer ▲ Hide answer
`async def` defines a coroutine function — calling it returns a coroutine object that doesn't execute until awaited or scheduled on the event loop. Inside, you can use `await`, `async for`, and `async with`. A regular `def` runs to completion synchronously when called. Mixing them naively (calling a coroutine without awaiting) is a common bug — you get a `RuntimeWarning: coroutine ... was never awaited`.
-
Q43
What does `await` do?
Medium▼ Show answer ▲ Hide answer
`await expr` suspends the current coroutine until `expr` (an awaitable — coroutine, Task, or Future) completes, returning control to the event loop so other tasks can run. When the awaited thing resolves, the coroutine resumes with the result. `await` is only legal inside `async def` functions.
pythonimport asyncio async def fetch(n): await asyncio.sleep(1) return n * 2 asyncio.run(asyncio.gather(fetch(1), fetch(2), fetch(3))) # ~1s totalOutput Idle -
Q44
What is the difference between concurrency and parallelism?
Medium▼ Show answer ▲ Hide answer
Concurrency means structuring a program so multiple tasks can be in progress at the same time — interleaved on one CPU, taking turns. Parallelism means actually executing multiple tasks at the same instant on multiple CPUs. Python threads (under the GIL) and `asyncio` give you concurrency; `multiprocessing`, C extensions that release the GIL, and the free-threaded build give you true parallelism.
-
Q45
What is `collections.defaultdict`?
Easy▼ Show answer ▲ Hide answer
`defaultdict(factory)` is a dict subclass that automatically creates missing keys by calling the factory (e.g., `list`, `int`, `set`). It removes boilerplate `if key not in d: d[key] = ...` patterns. Note that simply accessing a missing key creates the entry — use `dict.get(k, default)` if you don't want that side effect.
pythonfrom collections import defaultdict groups = defaultdict(list) for word in ['apple', 'ant', 'bee']: groups[word[0]].append(word) # {'a': ['apple', 'ant'], 'b': ['bee']}Output Idle -
Q46
What does `collections.Counter` do?
Easy▼ Show answer ▲ Hide answer
`Counter` is a dict subclass for counting hashable items. Built from any iterable, it exposes `most_common(k)`, supports arithmetic (`+`, `-`, `&`, `|`), and treats missing keys as count 0. It's the idiomatic tool for frequency tables, vote counts, and anagram detection.
pythonfrom collections import Counter c = Counter('mississippi') c.most_common(2) # [('i', 4), ('s', 4)] Counter('listen') == Counter('silent') # TrueOutput Idle -
Q47
What do `itertools.chain`, `groupby`, and `product` do?
Medium▼ Show answer ▲ Hide answer
`chain(*iterables)` flattens one level — concatenates iterables lazily. `groupby(iterable, key)` yields (key, group) for runs of consecutive items with the same key — the input must be sorted by that key first if you want true grouping. `product(*iterables, repeat=n)` is the Cartesian product — useful for nested-loop replacement.
pythonfrom itertools import chain, groupby, product list(chain([1, 2], [3])) # [1, 2, 3] [(k, list(g)) for k, g in groupby('aaabbc')] # [('a', [...]), ('b', [...]), ('c', [...])] list(product([0, 1], repeat=2)) # [(0,0),(0,1),(1,0),(1,1)]Output Idle -
Q48
What is EAFP vs LBYL?
Easy▼ Show answer ▲ Hide answer
EAFP ('Easier to Ask Forgiveness than Permission') — just try the operation and catch any exception. LBYL ('Look Before You Leap') — check preconditions first with explicit `if` tests. Python's culture and idioms strongly favor EAFP: it's race-free in concurrent code, often faster on the happy path, and reads more naturally.
python# EAFP (Pythonic) try: value = d[key] except KeyError: value = default # LBYL if key in d: value = d[key] else: value = defaultOutput Idle -
Q49
What is duck typing?
Easy▼ Show answer ▲ Hide answer
'If it walks like a duck and quacks like a duck, it's a duck.' Python cares about an object's behavior (the methods/attributes it supports) rather than its concrete type. Any object with a `read()` method works where a file-like object is expected, regardless of its class. Modern Python formalizes this with `typing.Protocol` (PEP 544) for static type checking.
-
Q50
What is `if __name__ == '__main__':` used for?
Easy▼ Show answer ▲ Hide answer
`__name__` is set to `'__main__'` when a file is run directly, and to the module's import name when imported. The `if __name__ == '__main__':` guard lets a file act both as a reusable module and as a script — code under the guard runs only when executed directly, not on import. It's also required for `multiprocessing` on Windows to avoid recursive process spawning.
pythondef main(): print('running directly') if __name__ == '__main__': main()Output Idle -
Q51
What are the key features of Python?
Easy▼ Show answer ▲ Hide answer
Python is high-level, interpreted, dynamically typed, and garbage-collected. Key features: clean readable syntax with significant whitespace, batteries-included standard library, first-class functions, everything-is-an-object data model, extensive third-party ecosystem (NumPy, Pandas, Django, etc.), and broad portability across operating systems. It supports multiple paradigms — procedural, object-oriented, and functional.
-
Q52
What is PEP 8 and why does it matter?
Easy▼ Show answer ▲ Hide answer
PEP 8 is the official Python style guide that defines conventions: 4-space indentation, 79-char line length, snake_case for functions/variables, PascalCase for classes, two blank lines between top-level definitions. Following it makes Python code instantly readable to any other Python developer. Tools like `flake8`, `black`, and `ruff` auto-check or auto-format to PEP 8.
-
Q53
Is Python interpreted or compiled?
Easy▼ Show answer ▲ Hide answer
Both. Python source is first compiled to bytecode (the `.pyc` files in `__pycache__`), then executed by the CPython virtual machine. So it's compiled to bytecode and interpreted at runtime — usually called 'interpreted' informally. PyPy adds a JIT compiler on top of that bytecode for big speedups.
-
Q54
CPython vs PyPy vs Jython vs IronPython — what's the difference?
Medium▼ Show answer ▲ Hide answer
CPython is the reference implementation written in C — what you get from python.org. PyPy is a Python implementation written in RPython with a JIT, often 4–10× faster on pure-Python code. Jython compiles to JVM bytecode and runs on the JVM. IronPython targets .NET / CLR. They mostly share the language spec but differ in performance and C-extension compatibility (CPython has the richest ecosystem).
-
Q55
Is Python statically or dynamically typed?
Easy▼ Show answer ▲ Hide answer
Dynamically typed — a variable has no fixed type; the type lives on the object, not the name. You can rebind the same name to different types in successive statements. Type hints (PEP 484) add optional static-checking via tools like mypy or pyright, but they don't change runtime behavior.
pythonx = 5; x = 'hi'; x = [1, 2] # all legal from typing import Optional def f(n: int) -> Optional[str]: ... # hint onlyOutput Idle -
Q56
Is Python strongly or weakly typed?
Easy▼ Show answer ▲ Hide answer
Strongly typed — Python does not silently coerce unrelated types. `'5' + 3` raises `TypeError`; you must explicitly convert with `int()` or `str()`. Contrast with JavaScript (`'5' + 3 == '53'`). The combination 'dynamic + strong' is Python's typing profile.
-
Q57
What are Python's built-in data types?
Easy▼ Show answer ▲ Hide answer
Numeric: `int`, `float`, `complex`, `bool`. Sequence: `list`, `tuple`, `range`, `str`, `bytes`, `bytearray`. Set: `set`, `frozenset`. Mapping: `dict`. None: `NoneType`. Plus callables (`function`, `method`, `class`), `module`, and many more in the standard library.
-
Q58
type() vs isinstance() — when to use which?
Easy▼ Show answer ▲ Hide answer
`type(x) is C` is an exact-class check — fails for subclasses. `isinstance(x, C)` returns True for `C` and any subclass, and accepts a tuple `(A, B)` for multi-class checks. Idiomatic Python prefers `isinstance` because it respects inheritance and ABCs.
pythonisinstance(True, int) # True — bool is a subclass type(True) is int # FalseOutput Idle -
Q59
What is a namespace and what is the LEGB rule?
Medium▼ Show answer ▲ Hide answer
A namespace is a mapping from names to objects (typically a `dict`). Python resolves a bare name using LEGB order: Local (current function), Enclosing (any outer functions), Global (module), Built-in (builtins module). The first match wins. `global` and `nonlocal` keywords let an inner scope rebind names in outer scopes.
pythonx = 'global' def outer(): x = 'enclosing' def inner(): print(x) # 'enclosing' via E inner()Output Idle -
Q60
What is the difference between a Python list and an array?
Easy▼ Show answer ▲ Hide answer
A `list` is heterogeneous and stores object references — flexible but uses more memory. The `array` module gives a homogeneous, compact, C-style typed array (`array('i', ...)`). NumPy `ndarray` is the standard for fast numeric work — contiguous memory, vectorized ops, broadcasting.
pythonfrom array import array arr = array('i', [1, 2, 3]) # compact ints import numpy as np a = np.array([1, 2, 3]) # vectorized mathOutput Idle -
Q61
What is the difference between `set` and `frozenset`?
Easy▼ Show answer ▲ Hide answer
Both store unique hashable items with O(1) membership. `set` is mutable (`.add`, `.remove`, `.update`); `frozenset` is immutable and therefore hashable — so it can live inside another set or be used as a dict key.
pythons = {1, 2}; s.add(3) # OK fs = frozenset({1, 2}) key = {fs: 'sets-of-sets ok'} # OK — frozenset is hashableOutput Idle -
Q62
Bytes vs bytearray vs str — what's the difference?
Medium▼ Show answer ▲ Hide answer
`str` is an immutable sequence of Unicode code points. `bytes` is an immutable sequence of integers 0–255. `bytearray` is the mutable version of bytes. Encode with `s.encode('utf-8')` to convert str → bytes; decode the reverse direction.
python'café'.encode('utf-8') # b'caf\xc3\xa9' bytearray(b'abc')[0] = 65 # mutable # bytes(b'abc')[0] = 65 # TypeErrorOutput Idle -
Q63
Are Python dicts ordered? Since when?
Easy▼ Show answer ▲ Hide answer
Yes — since Python 3.7 (and as a CPython implementation detail in 3.6), dictionaries preserve insertion order, and this is part of the language spec. `collections.OrderedDict` is now rarely needed, though it still offers extras like `move_to_end()` and equality that respects order.
-
Q64
How do you do type conversion in Python?
Easy▼ Show answer ▲ Hide answer
Use the type itself as a constructor: `int(x)`, `float(x)`, `str(x)`, `list(x)`, `tuple(x)`, `set(x)`, `dict(pairs)`, `bool(x)`. These raise `ValueError` / `TypeError` for invalid conversions. `int('0xff', 16)` accepts a base. Implicit coercion happens only between numeric types (e.g., `int + float → float`).
-
Q65
What do `//`, `%`, and `**` operators do?
Easy▼ Show answer ▲ Hide answer
`//` is floor division — returns the integer quotient (rounding toward negative infinity). `%` is the modulo (remainder). `**` is exponentiation; `pow(a, b, mod)` also exists for fast modular exponentiation.
python7 // 2 # 3 -7 // 2 # -4 — floors toward -inf 7 % 2 # 1 2 ** 10 # 1024Output Idle -
Q66
What does the slice `[::-1]` do?
Easy▼ Show answer ▲ Hide answer
It produces a reversed copy of any sequence. The slice is `[start:stop:step]` — leaving start/stop blank means the entire sequence, and step `-1` walks backward.
python'hello'[::-1] # 'olleh' [1, 2, 3][::-1] # [3, 2, 1]Output Idle -
Q67
Explain Python's negative indices.
Easy▼ Show answer ▲ Hide answer
Negative indices count from the end: `-1` is the last item, `-2` the second-last, etc. They work on any sequence (list, tuple, str). Slicing accepts negative bounds too: `s[-3:]` gets the last 3 items.
python[10, 20, 30, 40][-1] # 40 'hello'[-3:] # 'llo'Output Idle -
Q68
What do `pass`, `break`, and `continue` do?
Easy▼ Show answer ▲ Hide answer
`pass` is a no-op placeholder — useful where syntax requires a statement but you don't want to do anything (empty function, empty class). `break` exits the innermost loop immediately. `continue` skips the rest of the current iteration and proceeds to the next one.
pythonfor n in nums: if n < 0: continue # skip if n == sentinel: break process(n)Output Idle -
Q69
Does Python have a switch statement?
Easy▼ Show answer ▲ Hide answer
Before 3.10, no — idioms were `if/elif`, a dict-of-callables, or `getattr` dispatch. Python 3.10 added `match/case` (PEP 634), which is a structural-pattern-matching statement (much richer than C's switch — it can destructure, bind names, and match types).
pythonmatch command.split(): case ['quit']: print('bye') case ['move', x, y]: move(int(x), int(y)) case _: print('unknown')Output Idle -
Q70
Is Python case-sensitive?
Easy▼ Show answer ▲ Hide answer
Yes. `value`, `Value`, and `VALUE` are three distinct names. This applies to identifiers, attribute lookups, and module names. Filesystem case-sensitivity for module imports depends on the OS — Windows is case-insensitive on disk but Python itself isn't.
-
Q71
What is the difference between `id()`, `is`, and `==`?
Easy▼ Show answer ▲ Hide answer
`id(x)` returns the memory address (identity) of `x` in CPython. `a is b` is true iff `id(a) == id(b)` — same object. `a == b` calls `__eq__` and compares values. Use `is` only with singletons (`None`, `True`, `False`).
-
Q72
What is integer / string interning in Python?
Medium▼ Show answer ▲ Hide answer
CPython caches small integers (-5 to 256) and short, identifier-like strings in interpreter-wide tables so that equal values share the same object. This is why `a is b` can surprisingly return True for small ints — but it is an implementation detail you must never rely on for correctness; use `==` for values, `is` only for singletons.
pythona = 256; b = 256; a is b # True (cached) a = 257; b = 257; a is b # False (usually)Output Idle -
Q73
What are the conventions around `_` and `__` in names?
Easy▼ Show answer ▲ Hide answer
`_name` = 'weak private' — convention saying 'internal, don't touch'. `__name` (two leading) = name-mangled to `_ClassName__name` to avoid collisions in subclasses. `__name__` (dunder) = reserved for Python's special protocol (e.g., `__init__`, `__str__`). Lone `_` = throwaway variable; in the REPL, it also holds the last result.
pythonfor _ in range(3): print('hi') a, _, b = (1, 2, 3) # ignore middleOutput Idle -
Q74
What is a docstring? How is it different from a comment?
Easy▼ Show answer ▲ Hide answer
A docstring is the first string literal inside a module, class, or function — it becomes the object's `__doc__` attribute and is accessible at runtime via `help()`. A comment (`# ...`) is stripped by the parser and exists only in source. Use docstrings for API documentation, comments for implementation notes.
pythondef greet(name): """Return a friendly greeting for `name`.""" return f'Hi, {name}!' greet.__doc__ # the docstringOutput Idle -
Q75
What values are 'falsy' in Python?
Easy▼ Show answer ▲ Hide answer
`False`, `None`, `0`, `0.0`, `0j`, empty containers (`''`, `[]`, `()`, `{}`, `set()`, `range(0)`), and instances whose class defines `__bool__()` returning False (or `__len__()` returning 0). Everything else is truthy. Idiomatic Python uses `if items:` rather than `if len(items) > 0:`.
-
Q76
What is the walrus operator `:=`?
Medium▼ Show answer ▲ Hide answer
Added in 3.8 (PEP 572), `:=` is an assignment expression — assigns a value and returns it inline. Useful to compute once and reuse in a `while` test or comprehension filter, replacing duplicated calls or pre-loop reads.
pythonwhile (line := f.readline()): process(line) [y for x in data if (y := f(x)) is not None]Output Idle -
Q77
What are Python type hints / annotations?
Medium▼ Show answer ▲ Hide answer
Type hints (PEP 484, 526) declare expected types on function parameters, returns, and variables. They are NOT enforced at runtime — they live in `__annotations__` and are consumed by static checkers (mypy, pyright) and editors for autocomplete and bug detection. The `typing` module provides `Optional`, `Union`, `List`, `Dict`, `Callable`, `Protocol`, etc. Python 3.9+ allows `list[int]` directly; 3.10+ supports `X | Y` for union.
pythonfrom typing import Optional def parse(s: str) -> Optional[int]: try: return int(s) except ValueError: return NoneOutput Idle -
Q78
How do you write multi-line strings and multi-line comments?
Easy▼ Show answer ▲ Hide answer
Multi-line strings use triple quotes `"""..."""` (or `'''...'''`). Python has no real multi-line comment syntax — by convention you either prefix each line with `#`, or place a triple-quoted string literal whose value is ignored, but only at positions where a statement is legal.
-
Q79
What is tuple unpacking and starred assignment?
Easy▼ Show answer ▲ Hide answer
Unpacking assigns each element of an iterable to its own name in one statement. Starred `*name` collects the leftovers into a list. Works in function signatures, `for` loops, and assignments.
pythona, b, c = (1, 2, 3) first, *middle, last = [1, 2, 3, 4, 5] # 1, [2,3,4], 5 for k, v in d.items(): ...Output Idle -
Q80
How do you merge two dictionaries?
Easy▼ Show answer ▲ Hide answer
Three idiomatic ways: `{**a, **b}` (3.5+, returns a new dict, later keys win), `a | b` (3.9+, returns a new dict), or `a.update(b)` (mutates `a` in place). For deep merges (nested dicts), write a recursive helper — none of these do that for you.
pythona = {'x': 1, 'y': 2} b = {'y': 9, 'z': 3} {**a, **b} # {'x': 1, 'y': 9, 'z': 3} a | b # same, Python 3.9+Output Idle -
Q81
What is a class? What are attributes and methods?
Easy▼ Show answer ▲ Hide answer
A class is a blueprint for creating objects. Attributes are data bound to a class or instance (state); methods are functions defined inside the class that operate on that state. Class attributes are shared by all instances; instance attributes belong to one object and are normally set in `__init__`.
pythonclass Dog: species = 'Canis familiaris' # class attribute def __init__(self, name): self.name = name # instance attribute def bark(self): # method return f'{self.name} says woof'Output Idle -
Q82
Why is `self` explicit in Python?
Easy▼ Show answer ▲ Hide answer
`self` is the conventional name for the instance passed to a method. It's explicit because: (1) the Zen of Python prefers explicit over implicit, (2) it makes method-vs-function distinction syntactically clear, and (3) it lets you call methods as unbound functions when needed (`Klass.method(obj)`). Any name works, but using anything other than `self` is non-idiomatic.
-
Q83
Explain inheritance in Python with an example.
Easy▼ Show answer ▲ Hide answer
Inheritance lets a subclass reuse and extend a parent class. Python supports single, multiple, multi-level, and hierarchical inheritance. Use `super()` to invoke the parent's method during overriding.
pythonclass Animal: def __init__(self, name): self.name = name def speak(self): return '...' class Dog(Animal): def speak(self): return f'{self.name} barks' Dog('Rex').speak() # 'Rex barks'Output Idle -
Q84
What is polymorphism in Python?
Easy▼ Show answer ▲ Hide answer
Polymorphism = same interface, different behavior. Python achieves it via duck typing (objects with the right methods just work) and method overriding. Built-in example: `len()` works on any object that defines `__len__()` — strings, lists, dicts, sets, custom classes.
pythonclass Square: def area(self): return self.s ** 2 class Circle: def area(self): return 3.14 * self.r ** 2 for shape in shapes: print(shape.area()) # works for any 'shape'Output Idle -
Q85
How does encapsulation work in Python?
Medium▼ Show answer ▲ Hide answer
Python has no truly private attributes. By convention: `name` = public, `_name` = 'protected, don't touch from outside', `__name` = name-mangled to `_ClassName__name` to avoid accidental override in subclasses. Encapsulation is enforced by convention and `@property` accessors, not by the language.
pythonclass Account: def __init__(self, bal): self._balance = bal # 'protected' @property def balance(self): return self._balanceOutput Idle -
Q86
Does Python support method overloading?
Medium▼ Show answer ▲ Hide answer
Not by signature like Java/C++ — defining the same method twice simply replaces the first. Instead Python uses default arguments, `*args`/`**kwargs`, or `functools.singledispatch` for type-based dispatch. You can simulate overloading by branching on argument types or counts inside one function.
pythonfrom functools import singledispatch @singledispatch def area(shape): raise TypeError @area.register def _(s: int): return s * s # square side @area.register def _(s: tuple): return s[0] * s[1] # rectangleOutput Idle -
Q87
What is method overriding?
Easy▼ Show answer ▲ Hide answer
When a subclass defines a method with the same name as one in its parent, the subclass version overrides it for instances of that subclass. Use `super().method()` to also invoke the parent's behavior — common in `__init__` chains.
-
Q88
What is monkey patching?
Medium▼ Show answer ▲ Hide answer
Modifying or extending a class or module at runtime by reassigning its attributes from the outside. Useful in tests for stubbing out third-party functions and in libraries that need to patch behavior. Risky in production: silent, hard to debug, and tightly coupled to internals — prefer dependency injection or `unittest.mock`.
pythonimport requests orig_get = requests.get requests.get = lambda *a, **k: 'mocked' # monkey patch try: ... finally: requests.get = orig_getOutput Idle -
Q89
What is a singleton class? How do you implement one in Python?
Medium▼ Show answer ▲ Hide answer
A singleton is a class that has exactly one instance. The cleanest Python implementations: a module (modules are already singletons), a class with `__new__` that caches, or a `@singleton` decorator. Be cautious — singletons are global state in disguise and complicate testing.
pythonclass Settings: _inst = None def __new__(cls): if cls._inst is None: cls._inst = super().__new__(cls) return cls._instOutput Idle -
Q90
What is a metaclass?
Hard▼ Show answer ▲ Hide answer
A metaclass is 'the class of a class' — it controls how classes themselves are created, just as classes control how instances are created. The default metaclass is `type`. Subclass `type` and pass `metaclass=` in the class statement to customize. Used by ORMs (Django models), validators, and ABCs. The maxim: 'if you have to ask whether you need a metaclass, you don't.'
pythonclass Meta(type): def __new__(mcs, name, bases, ns): ns['created_by'] = 'Meta' return super().__new__(mcs, name, bases, ns) class Foo(metaclass=Meta): pass Foo.created_by # 'Meta'Output Idle -
Q91
Class variables vs instance variables — what's the difference?
Easy▼ Show answer ▲ Hide answer
Class variables are defined inside the class body and shared by every instance. Instance variables are set on `self` (usually in `__init__`) and belong to that one object. Watch out: assigning to a class variable through `self` creates an instance shadow, not an update — and using a mutable class variable as default state is a common bug.
pythonclass Cart: items = [] # SHARED — bug! def __init__(self): self.items = [] # per-instance — correctOutput Idle -
Q92
How do you overload Python operators?
Medium▼ Show answer ▲ Hide answer
Implement the corresponding dunder methods. `__add__` for `+`, `__sub__` for `-`, `__mul__`/`__rmul__`, `__eq__`/`__lt__` for comparisons, `__getitem__` for `[]`, `__call__` for `()`, `__len__` for `len()`, `__iter__` for `for`, `__contains__` for `in`. Provide `__hash__` whenever you define `__eq__` if instances need to be set/dict keys.
pythonclass Vector: def __init__(self, x, y): self.x, self.y = x, y def __add__(self, o): return Vector(self.x + o.x, self.y + o.y) def __repr__(self): return f'Vector({self.x}, {self.y})'Output Idle -
Q93
How can a class have multiple constructors?
Medium▼ Show answer ▲ Hide answer
Python only allows one `__init__`. Idiom: keep `__init__` minimal and provide alternative constructors as `@classmethod` factories — `Date.from_iso(...)`, `Date.from_timestamp(...)`. Each returns `cls(...)` so subclasses get the right type.
pythonclass Date: def __init__(self, y, m, d): self.y, self.m, self.d = y, m, d @classmethod def from_iso(cls, s): return cls(*map(int, s.split('-'))) @classmethod def today(cls): from datetime import date t = date.today(); return cls(t.year, t.month, t.day)Output Idle -
Q94
What is a `@dataclass`?
Medium▼ Show answer ▲ Hide answer
`@dataclasses.dataclass` (3.7+) auto-generates `__init__`, `__repr__`, `__eq__` (and optionally `__hash__`, `__lt__`, etc.) from class-level type-annotated attributes. Eliminates boilerplate for plain-data containers. Pass `frozen=True` for an immutable dataclass.
pythonfrom dataclasses import dataclass @dataclass(frozen=True) class Point: x: int y: int p = Point(1, 2); p.x # 1Output Idle -
Q95
What is the difference between `@dataclass` and `namedtuple`?
Medium▼ Show answer ▲ Hide answer
`namedtuple` (from `collections`) creates an immutable tuple subclass with named fields — lightweight, indexable, tuple-equal, supports unpacking. `@dataclass` creates a regular mutable class with named fields — adds methods, supports defaults, type hints, and `field(...)` customization. Use namedtuple for tiny immutable records; dataclass when you want mutation, methods, or default-factory fields.
-
Q96
What are descriptors in Python?
Hard▼ Show answer ▲ Hide answer
A descriptor is any class implementing `__get__`, `__set__`, and/or `__delete__`. When such an object lives on a class and you access it via an instance, Python invokes those methods instead of returning the descriptor itself. `@property`, `@classmethod`, `@staticmethod`, and ORM fields all use the descriptor protocol under the hood. Distinguishes data descriptors (with `__set__`) from non-data (only `__get__`) for attribute lookup priority.
pythonclass Positive: def __set_name__(self, owner, name): self.name = '_' + name def __get__(self, obj, owner): return getattr(obj, self.name) def __set__(self, obj, value): if value < 0: raise ValueError setattr(obj, self.name, value) class Account: balance = Positive()Output Idle -
Q97
What is a mixin class?
Medium▼ Show answer ▲ Hide answer
A mixin is a small class that provides reusable methods to be 'mixed into' other classes via multiple inheritance. It's not meant to be instantiated on its own and typically has no `__init__`. Example: `JsonSerializableMixin`, `LoggingMixin`. Mixins should be designed cooperatively (use `super()`) so MRO works cleanly.
pythonclass JsonMixin: def to_json(self): import json; return json.dumps(self.__dict__) class User(JsonMixin): def __init__(self, name): self.name = name User('Ada').to_json()Output Idle -
Q98
Difference between abstraction and encapsulation?
Medium▼ Show answer ▲ Hide answer
Abstraction = hiding *complexity* — exposing only the essential interface (what an object does). Encapsulation = hiding *internal state* — bundling data with the methods that operate on it and restricting direct access. ABCs / interfaces enable abstraction; `_private` + properties enable encapsulation. They reinforce each other but are not the same idea.
-
Q99
What is the diamond problem and how does Python solve it?
Hard▼ Show answer ▲ Hide answer
If class D inherits from B and C, both of which inherit from A, the question is: when calling D's method, which A is in play? Python's C3 linearization (the MRO) produces a deterministic order — each class appears before its parents, and the order in the bases list is preserved. `super()` follows the MRO, so cooperative `super().__init__()` chains call A exactly once.
-
Q100
How do you create an empty class?
Easy▼ Show answer ▲ Hide answer
Use a `pass` body, or a docstring, or `...`. Empty classes can be useful as namespaces, sentinels, or quick attribute bags.
pythonclass Empty: pass class Sentinel: ... class Config: """Bag of attributes."""Output Idle -
Q101
What is the difference between `__getattr__` and `__getattribute__`?
Hard▼ Show answer ▲ Hide answer
`__getattribute__` is invoked on EVERY attribute access — easy to break the class if you override it incorrectly (infinite recursion is the classic trap; call `super().__getattribute__(name)`). `__getattr__` is the fallback that runs only when normal lookup fails — much safer place to add dynamic attributes or proxy behavior.
pythonclass Proxy: def __init__(self, target): self._t = target def __getattr__(self, name): # only on miss return getattr(self._t, name)Output Idle -
Q102
Is Python pass-by-value or pass-by-reference?
Medium▼ Show answer ▲ Hide answer
Neither, strictly — Python is 'pass-by-object-reference' (sometimes called pass-by-assignment). The function receives a new local name bound to the same object. Mutating the object inside (e.g., `lst.append(x)`) is visible outside; rebinding the local (`lst = [...]`) is not.
pythondef f(lst, n): lst.append(99) # caller sees this n = 42 # caller does NOT see thisOutput Idle -
Q103
What is the difference between `global`, `nonlocal`, and local?
Medium▼ Show answer ▲ Hide answer
Names assigned inside a function are local by default. `global x` tells Python the name refers to the module-level `x` (assignments go there). `nonlocal x` (3.0+) targets the nearest enclosing function scope — used inside closures to mutate a captured name.
pythoncount = 0 def inc(): global count count += 1 def counter(): n = 0 def step(): nonlocal n n += 1 return n return stepOutput Idle -
Q104
What are higher-order functions? Give examples.
Easy▼ Show answer ▲ Hide answer
A higher-order function takes a function as argument and/or returns a function. Built-ins: `map`, `filter`, `sorted(..., key=...)`, `min`/`max(..., key=...)`, `functools.reduce`. Decorators are also higher-order functions.
pythonsorted(words, key=str.lower) list(map(str.upper, words)) list(filter(str.isalpha, tokens))Output Idle -
Q105
What's the difference between `map`, `filter`, and `reduce`?
Easy▼ Show answer ▲ Hide answer
`map(fn, iter)` applies `fn` to each item, producing an iterator of results. `filter(pred, iter)` keeps items where `pred(item)` is truthy. `reduce(fn, iter, init)` (in `functools`) folds the iterable into a single value by repeated application. List comprehensions and generator expressions are usually clearer than `map`/`filter` in Python.
pythonlist(map(lambda x: x*x, [1,2,3])) # [1,4,9] list(filter(lambda x: x>1, [1,2,3])) # [2,3] from functools import reduce reduce(lambda a,b: a+b, [1,2,3,4], 0) # 10Output Idle -
Q106
What do `zip` and `enumerate` do?
Easy▼ Show answer ▲ Hide answer
`zip(*iters)` pairs items position-wise into tuples, stopping at the shortest. `zip_longest` (in `itertools`) fills with a default. `enumerate(iter, start=0)` yields `(index, item)` pairs — the Pythonic alternative to `range(len(...))`.
pythonfor i, name in enumerate(names, 1): print(i, name) list(zip([1, 2], ['a', 'b'])) # [(1, 'a'), (2, 'b')]Output Idle -
Q107
What is the mutable default argument trap?
Medium▼ Show answer ▲ Hide answer
Default arguments are evaluated ONCE, at function-definition time. A mutable default (list, dict, set) is shared across every call. Fix: use `None` and create a fresh container inside.
pythondef buggy(item, bag=[]): # BAD bag.append(item) return bag buggy(1); buggy(2) # [1, 2] — surprise! def fixed(item, bag=None): if bag is None: bag = [] bag.append(item) return bagOutput Idle -
Q108
Explain late binding in Python closures.
Hard▼ Show answer ▲ Hide answer
Closures capture variables by reference, not by value — when the inner function runs, it looks up the current value of the captured name. Inside a loop this often surprises people: every closure sees the loop variable's final value. Fix by capturing the value as a default argument: `lambda x=x: ...`
pythonfs = [lambda: i for i in range(3)] [f() for f in fs] # [2, 2, 2] — all see final i fs = [lambda x=i: x for i in range(3)] [f() for f in fs] # [0, 1, 2] — fixedOutput Idle -
Q109
What is recursion? What is Python's recursion limit?
Easy▼ Show answer ▲ Hide answer
Recursion = a function calling itself, with a base case to stop. Python sets `sys.getrecursionlimit()` to ~1000 by default to prevent runaway recursion blowing the C stack. Raise with `sys.setrecursionlimit(N)` — but heavy recursion in Python is slow; iterative or `functools.lru_cache` solutions usually win.
pythonimport sys; sys.getrecursionlimit() # 1000 sys.setrecursionlimit(10_000)Output Idle -
Q110
What is `functools.partial`?
Medium▼ Show answer ▲ Hide answer
`partial(fn, *args, **kwargs)` returns a new callable with some arguments pre-filled. Useful to adapt a function's signature for callbacks or `key=` parameters without writing a lambda.
pythonfrom functools import partial int2 = partial(int, base=2) int2('1010') # 10Output Idle -
Q111
How do you write a decorator that takes arguments?
Medium▼ Show answer ▲ Hide answer
A decorator with arguments is a 3-level function: outer accepts the args, middle accepts the function, inner is the wrapper. The outer is called first to produce the actual decorator.
pythonfrom functools import wraps def retry(times): def deco(fn): @wraps(fn) def wrap(*a, **kw): for _ in range(times - 1): try: return fn(*a, **kw) except Exception: pass return fn(*a, **kw) return wrap return deco @retry(times=3) def flaky(): ...Output Idle -
Q112
Can a class be used as a decorator?
Medium▼ Show answer ▲ Hide answer
Yes — any callable can decorate. A class decorator stores the wrapped function in `__init__` and acts as the wrapper via `__call__`. Useful when you need stateful decoration (counters, registries) without nested closures.
pythonclass Counter: def __init__(self, fn): self.fn = fn; self.calls = 0 def __call__(self, *a, **kw): self.calls += 1 return self.fn(*a, **kw) @Counter def hi(): print('hi') hi(); hi(); hi.calls # 2Output Idle -
Q113
What is the order when multiple decorators are stacked?
Medium▼ Show answer ▲ Hide answer
Decorators apply bottom-up: the one closest to `def` runs first, then the next outward wraps the result. Calling the final function runs them in the reverse (top-down) order.
python@A # applied last → runs first on call @B @C # applied first → innermost def f(): ... # Equivalent to: f = A(B(C(f)))Output Idle -
Q114
What is a list comprehension? Show the syntax.
Easy▼ Show answer ▲ Hide answer
A list comprehension builds a list from an iterable in a single expression: `[expr for x in iter if cond]`. Faster and clearer than a manual loop with `append`. Avoid them when the body has side effects or grows past one line of logic.
python[x*x for x in range(10) if x % 2 == 0] # [0, 4, 16, 36, 64]Output Idle -
Q115
How do you write a dict comprehension?
Easy▼ Show answer ▲ Hide answer
`{key_expr: val_expr for x in iter if cond}` — same shape as list comp but with `:` instead of comma.
python{w: len(w) for w in ['a', 'bb', 'ccc']} # {'a': 1, 'bb': 2, 'ccc': 3} {v: k for k, v in d.items()} # invert a dictOutput Idle -
Q116
How do you write a set comprehension?
Easy▼ Show answer ▲ Hide answer
`{expr for x in iter if cond}` — same as list comp but with curly braces and no key:value.
python{w.lower() for w in words} # unique lowercasedOutput Idle -
Q117
Generator expression vs list comprehension — which to use?
Medium▼ Show answer ▲ Hide answer
Same syntax with `( )` instead of `[ ]`. A list comp builds the full list eagerly (O(n) memory). A genexp yields lazily (O(1) memory) — perfect for streaming, large data, or feeding into reducers like `sum`, `any`, `max`. If you need indexing, slicing, or repeated iteration, you need a list.
pythonsum(x*x for x in range(10**8)) # genexp — constant memory [x*x for x in range(10**8)] # list — likely OOMOutput Idle -
Q118
How do generator `.send()` and `.throw()` work?
Hard▼ Show answer ▲ Hide answer
`.send(value)` resumes a paused generator and makes its `yield` expression evaluate to `value` instead of None. `.throw(exc)` raises the exception inside the generator at its yield point — used to signal it to clean up or change state. These are the foundation of pre-asyncio coroutines.
pythondef echo(): while True: msg = yield print('got:', msg) g = echo(); next(g); g.send('hi') # got: hiOutput Idle -
Q119
What is `StopIteration` and when is it raised?
Easy▼ Show answer ▲ Hide answer
`StopIteration` signals the end of an iterator — `next()` raises it when there is nothing more to yield, and `for` loops catch it silently. In a generator function, `return` (or running off the end) raises `StopIteration`. Returning a value from a generator stores it on the exception (`e.value`).
-
Q120
How do `iter()` and `next()` work?
Easy▼ Show answer ▲ Hide answer
`iter(obj)` returns an iterator — it calls `obj.__iter__()`. `next(it)` advances it — it calls `it.__next__()`. `next(it, default)` returns `default` instead of raising `StopIteration`. The two-argument `iter(callable, sentinel)` form keeps calling `callable()` until it returns `sentinel`.
pythonwith open('log') as f: for line in iter(f.readline, ''): # read until EOF process(line)Output Idle -
Q121
Show how to build a custom iterator class.
Medium▼ Show answer ▲ Hide answer
Implement `__iter__` (returns self) and `__next__` (returns next value or raises `StopIteration`). A generator is almost always shorter — but custom iterator classes shine when you need extra methods or rich internal state.
pythonclass Counter: def __init__(self, n): self.i, self.n = 0, n def __iter__(self): return self def __next__(self): if self.i >= self.n: raise StopIteration self.i += 1; return self.iOutput Idle -
Q122
What do `itertools.combinations` and `permutations` do?
Medium▼ Show answer ▲ Hide answer
`permutations(iter, r)` yields every ordered arrangement of r items — n! / (n-r)!. `combinations(iter, r)` yields every unordered selection — n choose r. `combinations_with_replacement` allows repeats. All are lazy iterators.
pythonfrom itertools import permutations, combinations list(permutations([1, 2, 3], 2)) # [(1,2),(1,3),(2,1),(2,3),(3,1),(3,2)] list(combinations([1, 2, 3], 2)) # [(1,2),(1,3),(2,3)]Output Idle -
Q123
What do `all()` and `any()` do?
Easy▼ Show answer ▲ Hide answer
`all(iter)` is True iff every item is truthy (vacuously True on an empty iterable). `any(iter)` is True iff at least one item is truthy (False on empty). Both short-circuit — they stop as soon as the answer is known.
pythonall(x > 0 for x in nums) any(s.startswith('!') for s in lines)Output Idle -
Q124
Why isn't all memory freed when Python exits?
Medium▼ Show answer ▲ Hide answer
Some objects with circular references or module-level cleanup ordering issues may not be reclaimed — the interpreter relies on the OS to free that memory at process exit. Other reasons: C-extension globals, the small-object allocator's arenas, and objects held by `atexit` handlers. For long-running services, tracking real leaks needs `tracemalloc` or `objgraph`, not exit-time inspection.
-
Q125
What is the `gc` module? When should you use it?
Medium▼ Show answer ▲ Hide answer
`gc` exposes Python's cyclic garbage collector — the one that catches reference cycles refcounting can't. APIs: `gc.collect()` to force a run, `gc.disable()/enable()`, `gc.get_threshold()` / `set_threshold()` for generational tuning, `gc.get_objects()` for inspection. Disabling can speed up tight allocation loops; tuning helps long-running servers.
pythonimport gc gc.collect() # force a cycle collection gc.set_threshold(700, 10, 10)Output Idle -
Q126
What is `weakref`?
Medium▼ Show answer ▲ Hide answer
A weak reference points to an object without preventing it from being garbage-collected. Useful for caches, observer patterns, and breaking reference cycles. When the target is freed, the weakref becomes `None`.
pythonimport weakref class Big: ... obj = Big(); r = weakref.ref(obj) r() # <Big object> del obj r() # None — gc'dOutput Idle -
Q127
How do you profile Python code?
Medium▼ Show answer ▲ Hide answer
Time: `cProfile` for function-level breakdown, `timeit` for micro-benchmarks, `pyinstrument` for flamegraphs, `line_profiler` for line-by-line, `Scalene` for combined CPU+memory. Memory: `tracemalloc` (stdlib) or `memory_profiler`. Always profile before optimizing — intuition about Python performance is usually wrong.
pythonimport cProfile cProfile.run('main()', sort='cumulative')Output Idle -
Q128
What does `sys.getsizeof` actually measure?
Medium▼ Show answer ▲ Hide answer
It reports the size of the object itself, NOT the objects it references. A list of strings reports its own array overhead, not the strings inside. Use `pympler.asizeof` for deep size accounting.
pythonimport sys sys.getsizeof([]) # 56 (empty list overhead) sys.getsizeof([1, 2, 3]) # 88 — but doesn't count the intsOutput Idle -
Q129
What is the Python exception hierarchy?
Medium▼ Show answer ▲ Hide answer
Root: `BaseException` → `Exception` (catch this in `except:`, never `BaseException`) → standard categories like `ArithmeticError`, `LookupError`, `OSError`. `BaseException` also covers `SystemExit`, `KeyboardInterrupt`, and `GeneratorExit` — exceptions you generally do NOT want to catch with a bare `except:`.
-
Q130
What's the difference between `raise` and `raise from`?
Medium▼ Show answer ▲ Hide answer
`raise X` raises a new exception. `raise X from Y` does the same but explicitly sets `X.__cause__` to `Y`, producing a clean chained traceback ('The above exception was the direct cause of...'). Use it when translating a low-level error into a domain-specific one.
pythontry: int(s) except ValueError as e: raise InvalidInput(f'bad: {s}') from eOutput Idle -
Q131
What is the `assert` statement and when should you use it?
Easy▼ Show answer ▲ Hide answer
`assert expr [, message]` raises `AssertionError` if `expr` is falsy. Use it for invariants and developer-time sanity checks — never for user-facing input validation, because asserts are stripped when Python is run with `-O` (optimize). For validation, raise `ValueError` / `TypeError` explicitly.
pythondef divide(a, b): assert b != 0, 'b must be non-zero' # dev check return a / bOutput Idle -
Q132
How do you catch multiple exception types at once?
Easy▼ Show answer ▲ Hide answer
Use a tuple of exception classes: `except (TypeError, ValueError) as e:`. You can chain `except` blocks; the first matching one runs. Python 3.11 added `except*` for exception groups (PEP 654) used by asyncio's `gather`.
pythontry: int(s) except (TypeError, ValueError) as e: log.warning(e)Output Idle -
Q133
What is `try/finally` and how does it differ from `else`?
Easy▼ Show answer ▲ Hide answer
`finally` runs ALWAYS — even if the `try` block returned, raised, or `continue`'d. Use for cleanup that must happen no matter what (closing handles, releasing locks). `else` runs only if no exception was raised in `try` — use it to scope the success path tightly so it isn't accidentally caught by `except`.
-
Q134
What is a `.pyc` file and what is `__pycache__`?
Easy▼ Show answer ▲ Hide answer
When you import a module, CPython compiles the source to bytecode and caches it as a `.pyc` file in `__pycache__/`. On subsequent imports, if the source hasn't changed (mtime check), the cached bytecode is loaded directly — skipping the parse step. Safe to delete; will be regenerated.
-
Q135
What is `PYTHONPATH`?
Easy▼ Show answer ▲ Hide answer
An environment variable listing additional directories Python searches for modules, prepended to `sys.path` at startup. Setting it lets you import code outside the current directory or installed packages. In modern projects, installing your package (`pip install -e .`) is preferred over manipulating `PYTHONPATH`.
-
Q136
What is the difference between a module and a package?
Easy▼ Show answer ▲ Hide answer
A module is a single `.py` file. A package is a directory containing an `__init__.py` (or treated as a namespace package since 3.3) that groups related modules. Packages can be nested, forming dotted import paths like `mypkg.submod.thing`.
-
Q137
What is a namespace package (PEP 420)?
Medium▼ Show answer ▲ Hide answer
A package split across multiple directories — no `__init__.py` required. Python collects all matching paths from `sys.path` and merges them into one logical package. Used by plugin ecosystems (e.g., `zope.*`) where multiple distributions contribute submodules to the same top-level name.
-
Q138
What is `importlib` and when do you use it?
Medium▼ Show answer ▲ Hide answer
The standard library's import machinery exposed as a module. `importlib.import_module('a.b')` imports by a string name (useful for plugins, config-driven dispatch). `importlib.reload(mod)` re-imports a module after editing — handy in notebooks and REPLs.
pythonimport importlib mod = importlib.import_module(cfg['driver'])Output Idle -
Q139
What is the difference between `pip` and `conda`?
Easy▼ Show answer ▲ Hide answer
`pip` installs Python packages from PyPI into the active Python environment. `conda` is a cross-language package + environment manager from Anaconda — handles non-Python binaries (CUDA, BLAS, R) and creates isolated environments without needing `venv`. Modern alternative: `uv` (very fast pip replacement) and `poetry` (deps + venv + publishing).
-
Q140
What's the difference between `setup.py`, `pyproject.toml`, and `requirements.txt`?
Medium▼ Show answer ▲ Hide answer
`requirements.txt` is a flat list of dependencies for `pip install -r` — for apps, not for libraries. `setup.py` was the historical way to declare package metadata (executable Python). `pyproject.toml` (PEP 517/518/621) is the modern declarative replacement — works with any build backend (setuptools, hatch, poetry, flit). New projects should use `pyproject.toml` alone.
-
Q141
What is the `threading` module's `Lock`?
Medium▼ Show answer ▲ Hide answer
A `Lock` is a mutex — only one thread can hold it at a time. Use it to guard a critical section that mutates shared state, preventing races. Prefer `with lock:` over manual acquire/release so the lock is freed even on exceptions. `RLock` allows the same thread to re-acquire; `Semaphore` allows N holders.
pythonimport threading lock = threading.Lock() with lock: shared.append(x)Output Idle -
Q142
`concurrent.futures` — ThreadPoolExecutor vs ProcessPoolExecutor?
Medium▼ Show answer ▲ Hide answer
Both provide a uniform `submit` / `map` API for parallel work. `ThreadPoolExecutor` shares memory and is ideal for I/O-bound tasks (sockets, files). `ProcessPoolExecutor` spawns processes — true CPU parallelism, but objects must be picklable and there's serialization overhead. Workers are reused, unlike raw `threading.Thread` / `multiprocessing.Process`.
pythonfrom concurrent.futures import ThreadPoolExecutor with ThreadPoolExecutor(8) as ex: results = list(ex.map(fetch, urls))Output Idle -
Q143
What's the difference between `asyncio.gather`, `asyncio.wait`, and `asyncio.as_completed`?
Hard▼ Show answer ▲ Hide answer
`gather(*tasks)` returns results in input order; can `return_exceptions=True` to swallow errors. `wait(...)` returns (done, pending) sets and is more flexible (`FIRST_COMPLETED`, timeouts). `as_completed(tasks)` is an iterator that yields each task as it finishes — useful for streaming results. Python 3.11 added `asyncio.TaskGroup` (PEP 654) as a structured-concurrency-friendly replacement for `gather`.
-
Q144
What is the `queue` module used for?
Easy▼ Show answer ▲ Hide answer
Thread-safe FIFO/LIFO/priority queues for producer-consumer patterns between threads — `Queue`, `LifoQueue`, `PriorityQueue`. `multiprocessing.Queue` is the analog for processes. `asyncio.Queue` is the awaitable version. Each pops items via `.get()` and pushes via `.put()`, with `.join()` and `.task_done()` for completion tracking.
-
Q145
What is a daemon thread?
Medium▼ Show answer ▲ Hide answer
A thread whose presence does NOT keep the program alive — when only daemon threads remain, the interpreter exits and they are killed abruptly. Set with `t.daemon = True` (before `start()`). Use for background workers like log shippers or heartbeats; never for tasks that need to clean up (they get no chance).
pythonimport threading t = threading.Thread(target=heartbeat, daemon=True) t.start()Output Idle -
Q146
What does `collections.OrderedDict` give you that `dict` doesn't?
Medium▼ Show answer ▲ Hide answer
Since 3.7, regular dicts preserve insertion order, so OrderedDict is rarely needed. It still offers `move_to_end(key, last=True)`, equality that respects key order, and `popitem(last=False)` to pop from the front — useful for LRU-cache-style structures.
-
Q147
What is `collections.namedtuple`?
Easy▼ Show answer ▲ Hide answer
A factory for immutable tuple subclasses with named, indexable fields. Lightweight (no `__dict__`), tuple-equal, unpackable. Replaced in many places by `@dataclass(frozen=True)` for richer features.
pythonfrom collections import namedtuple Point = namedtuple('Point', 'x y') p = Point(1, 2); p.x, p[0] # both 1Output Idle -
Q148
What is `collections.deque`?
Easy▼ Show answer ▲ Hide answer
A double-ended queue with O(1) `append`, `pop`, `appendleft`, `popleft`. Use it whenever you need queue or sliding-window behavior — a `list.pop(0)` is O(n) but `deque.popleft()` is O(1). Also supports a maxlen for fixed-size sliding windows.
pythonfrom collections import deque q = deque(maxlen=3) for x in [1,2,3,4]: q.append(x) list(q) # [2, 3, 4]Output Idle -
Q149
What does `functools.reduce` do?
Easy▼ Show answer ▲ Hide answer
`reduce(fn, iter, init)` repeatedly applies `fn(accum, x)` left-to-right, folding the iterable into a single value. Built-ins like `sum`, `max`, `''.join` are usually clearer for the common cases. Useful when those don't fit (e.g., merging multiple dicts).
pythonfrom functools import reduce reduce(lambda a, b: {**a, **b}, list_of_dicts, {})Output Idle -
Q150
What is the `datetime` module?
Easy▼ Show answer ▲ Hide answer
Provides `date`, `time`, `datetime`, `timedelta`, and `timezone` classes for date/time arithmetic. Always prefer timezone-aware `datetime` (`tz=timezone.utc`) over naive ones — naive datetimes mixed with aware ones raise `TypeError`. For richer parsing/formatting, the third-party `arrow`, `pendulum`, or `dateutil` libraries are popular.
pythonfrom datetime import datetime, timezone, timedelta now = datetime.now(timezone.utc) tomorrow = now + timedelta(days=1)Output Idle -
Q151
How do you read/write JSON in Python?
Easy▼ Show answer ▲ Hide answer
`json.dumps(obj)` → string; `json.loads(s)` → object; `json.dump(obj, f)` / `json.load(f)` for files. JSON supports only str-keyed dicts, lists, strings, numbers, booleans, and None — custom objects need `default=...` (or `cls=`) for serialization and `object_hook=...` for deserialization.
pythonimport json json.dumps({'a': 1, 'b': [1, 2]}) json.loads('{"a": 1}')Output Idle -
Q152
What is `pickle` and what are its risks?
Medium▼ Show answer ▲ Hide answer
`pickle` serializes Python objects to bytes and back — handles cyclic refs, custom classes, etc. NEVER unpickle data from an untrusted source: a malicious pickle can execute arbitrary code on `loads`. For interchange between systems or untrusted input, use JSON, MessagePack, or Protocol Buffers.
pythonimport pickle b = pickle.dumps(obj) obj2 = pickle.loads(b) # only on trusted input!Output Idle -
Q153
What is the `pathlib` module?
Easy▼ Show answer ▲ Hide answer
Object-oriented filesystem paths — `Path('a/b').exists()`, `p / 'subdir' / 'file.txt'`, `p.read_text()`, `p.glob('*.py')`. Modern replacement for most `os.path` string-juggling. Cross-platform: same code works on Windows and POSIX.
pythonfrom pathlib import Path root = Path(__file__).parent for py in root.rglob('*.py'): print(py.relative_to(root))Output Idle -
Q154
What is the `re` module? Show common operations.
Medium▼ Show answer ▲ Hide answer
Regex engine. `re.match` checks beginning, `re.search` scans anywhere, `re.findall` returns all matches, `re.sub` replaces. `re.compile(pat)` builds a reusable pattern (cheaper if used in a loop). Use raw strings `r'\d+'` to avoid double-escaping.
pythonimport re re.findall(r'\d+', 'a1 b22 c333') # ['1', '22', '333'] re.sub(r'\s+', ' ', ' hi there ') # ' hi there 'Output Idle -
Q155
What does the `logging` module give you over `print`?
Easy▼ Show answer ▲ Hide answer
Levels (DEBUG/INFO/WARNING/ERROR/CRITICAL), per-module loggers, structured formatters, handlers that route logs to files / stdout / syslog / Sentry, and runtime-tunable verbosity. `print` is fine for scripts; any non-trivial app or library should use `logging`.
pythonimport logging logging.basicConfig(level=logging.INFO) log = logging.getLogger(__name__) log.info('starting up')Output Idle -
Q156
What is the `typing` module?
Medium▼ Show answer ▲ Hide answer
Provides generic type-hint primitives: `Optional`, `Union` (now `X | Y`), `List`, `Dict`, `Tuple`, `Callable`, `Iterable`, `Any`, `TypeVar`, `Protocol` (structural typing / duck-type interfaces). Used by static checkers (mypy, pyright) — has zero runtime effect on dispatch.
pythonfrom typing import Callable, Optional def apply(f: Callable[[int], int], x: int) -> Optional[int]: try: return f(x) except Exception: return NoneOutput Idle -
Q157
What is the `argparse` module?
Easy▼ Show answer ▲ Hide answer
Standard library CLI argument parser. Declarative: define args with `add_argument`, get `--help` and type coercion for free.
pythonimport argparse p = argparse.ArgumentParser() p.add_argument('--name', required=True) p.add_argument('-n', '--count', type=int, default=1) args = p.parse_args()Output Idle -
Q158
What does `heapq` do?
Easy▼ Show answer ▲ Hide answer
Min-heap (priority queue) operations on a regular list: `heapify`, `heappush`, `heappop` — all O(log n). For a max-heap, push negated values. `heapq.nlargest(k, iter)` / `nsmallest(k, iter)` are O(n log k) — perfect for 'top K' problems.
pythonimport heapq h = [] for x in [3, 1, 4, 1, 5]: heapq.heappush(h, x) heapq.heappop(h) # 1Output Idle -
Q159
What is `dataclasses.field` used for?
Medium▼ Show answer ▲ Hide answer
Customizes per-attribute behavior in a dataclass: `default_factory` for mutable defaults (lists, dicts), `init=False` to skip from __init__, `repr=False`, `compare=False`, metadata. The mutable-default-arg trap also applies to dataclasses — `field(default_factory=list)` is the fix.
pythonfrom dataclasses import dataclass, field @dataclass class Cart: items: list = field(default_factory=list) # safeOutput Idle -
Q160
What does `os` vs `sys` vs `pathlib` cover?
Easy▼ Show answer ▲ Hide answer
`os` = OS-level operations (env vars, processes, filesystem ops, fork). `sys` = interpreter / runtime (argv, stdin/stdout/stderr, exit, path, modules). `pathlib` = OO path manipulation (mostly replacing `os.path`). For modern code: use `pathlib` for paths, `os.environ` for env, `sys.argv`/`sys.exit` for entrypoints.
-
Q161
What is the best way to concatenate many strings?
Easy▼ Show answer ▲ Hide answer
Use `''.join(iterable)` — one allocation, O(n) total. Using `+` in a loop creates a new string every iteration (O(n²)). For two or three strings, `+` or an f-string is fine.
python''.join(parts) # best for many 'a' + 'b' + 'c' # fine for few f'{user}: {message}' # best for templatesOutput Idle -
Q162
What are the most useful string methods?
Easy▼ Show answer ▲ Hide answer
`split` / `rsplit`, `join`, `strip` / `lstrip` / `rstrip`, `lower` / `upper` / `title` / `casefold`, `startswith` / `endswith`, `find` / `rfind` (returns -1 if missing) vs `index` (raises), `replace`, `count`, `isdigit` / `isalpha` / `isalnum`, `format`, `zfill`, `partition`.
python' Hello World '.strip().lower().split() # ['hello', 'world'] ','.join(['a', 'b', 'c']) # 'a,b,c'Output Idle -
Q163
What are `chr()` and `ord()`?
Easy▼ Show answer ▲ Hide answer
`ord(c)` returns the Unicode code point of a single character. `chr(n)` returns the character for a code point. Useful for character arithmetic (Caesar ciphers, position-in-alphabet) and ASCII range checks.
pythonord('A'), chr(65) # (65, 'A') ord('a') - ord('A') # 32Output Idle -
Q164
What is the difference between `str.find` and `str.index`?
Easy▼ Show answer ▲ Hide answer
Both search for a substring. `find` returns -1 if not present; `index` raises `ValueError`. Choose based on whether 'not found' is exceptional or expected. There's also `rfind`/`rindex` for searching from the right.
-
Q165
How do you encode and decode between `str` and `bytes`?
Medium▼ Show answer ▲ Hide answer
`s.encode('utf-8')` → bytes; `b.decode('utf-8')` → str. UTF-8 is the universal default. On encoding errors, pass `errors='ignore'` to drop or `errors='replace'` to substitute `\ufffd`. Always be explicit about encoding when reading files — never trust `locale.getpreferredencoding()`.
-
Q166
How do you read a file safely in Python?
Easy▼ Show answer ▲ Hide answer
Use `with open(path, encoding='utf-8') as f:` so the file is closed automatically — even on exception. For text, iterate line by line (`for line in f:`) — it's memory-efficient and the canonical idiom.
pythonwith open('big.log', encoding='utf-8') as f: for line in f: if 'ERROR' in line: handle(line)Output Idle -
Q167
How do you read a large file efficiently?
Medium▼ Show answer ▲ Hide answer
Iterate line-by-line — `for line in f:` reads buffered lines lazily. For binary streams, read in fixed-size chunks (`while chunk := f.read(64 * 1024):`). NEVER call `f.read()` on a multi-GB file unless you have the RAM.
pythonwith open('huge.bin', 'rb') as f: while chunk := f.read(65536): process(chunk)Output Idle -
Q168
How do you read/write CSV files?
Easy▼ Show answer ▲ Hide answer
Use the `csv` module — `csv.reader(f)` / `csv.writer(f)`, or `csv.DictReader` / `DictWriter` for header-aware access. Pass `newline=''` to `open` when writing on Windows to avoid blank-line bugs. For heavy CSV work, `pandas.read_csv` is faster and more flexible.
pythonimport csv with open('data.csv', newline='', encoding='utf-8') as f: for row in csv.DictReader(f): print(row['name'])Output Idle -
Q169
What is a raw string `r"..."`?
Easy▼ Show answer ▲ Hide answer
A raw string disables backslash escapes — `r'\n'` is two characters (`\` and `n`), not a newline. Use them for regex patterns, Windows paths, and any string where backslashes appear literally.
pythonr'C:\users\me' re.compile(r'\d{3}-\d{4}')Output Idle -
Q170
How do you format numbers and align text in f-strings?
Medium▼ Show answer ▲ Hide answer
Use the format spec after a colon: `{value:fmt}`. Width, alignment (`<`, `>`, `^`), fill, precision, thousands, base. `=` for debug.
pythonf'{3.14159:.2f}' # '3.14' f'{1234567:,}' # '1,234,567' f'{name:<10}|{age:>3}' # left/right align f'{255:08b}' # '11111111'Output Idle -
Q171
How do you write to a file atomically?
Medium▼ Show answer ▲ Hide answer
Write to a temp file in the same directory, then `os.replace(tmp, dest)` — `replace` is atomic on POSIX and Windows. Prevents readers from seeing a half-written file if your process is killed.
pythonimport os, tempfile fd, tmp = tempfile.mkstemp(dir=os.path.dirname(dest)) with os.fdopen(fd, 'w') as f: f.write(data) os.replace(tmp, dest)Output Idle -
Q172
What's the difference between `os.path` and `pathlib`?
Easy▼ Show answer ▲ Hide answer
`os.path` is the older string-based API (`os.path.join`, `os.path.exists`). `pathlib` is the modern OO replacement — `Path` objects with operators (`p / 'sub'`) and rich methods (`.read_text`, `.glob`, `.stem`). Prefer `pathlib` for new code; both are first-class and interchangeable via `str(Path(...))`.
-
Q173
How does Python's truthy/falsy with short-circuit work?
Easy▼ Show answer ▲ Hide answer
`and` returns the first falsy operand (or the last operand if all truthy); `or` returns the first truthy. They short-circuit — the right operand is only evaluated if needed. Common idioms: `x or default`, `a and a[0]` (safe lookup).
pythonname = user_input or 'anonymous' first = items and items[0] # None-safeOutput Idle -
Q174
How does the `for ... else` clause work?
Medium▼ Show answer ▲ Hide answer
The `else` block on a loop runs when the loop completes without hitting a `break`. Reads as 'no-break' — useful for search loops where you want a 'not found' branch right next to the search itself. Same applies to `while ... else`.
pythonfor item in items: if item == target: print('found'); break else: print('not found')Output Idle -
Q175
Why prefer `enumerate` over `range(len(...))`?
Easy▼ Show answer ▲ Hide answer
Clearer intent, fewer indexing bugs, works with any iterable (not just sized sequences), and the indexing through `lst[i]` is slower in CPython than direct iteration. `enumerate` also lets you start the count at any value with `start=...`.
python# Pythonic for i, name in enumerate(names, start=1): print(i, name) # Not Pythonic for i in range(len(names)): print(i + 1, names[i])Output Idle -
Q176
What is the difference between `is` and `==` for `None`?
Easy▼ Show answer ▲ Hide answer
Always use `x is None` — there is exactly one `None` object, and `is` is faster (pure pointer comparison) and unambiguous. `x == None` could be overridden by `__eq__` to do something unexpected. PEP 8 mandates `is None`.
-
Q177
What is dictionary unpacking `{**a, **b}`?
Easy▼ Show answer ▲ Hide answer
Merges dicts into a new dict; later keys overwrite earlier ones. Works in function calls (`f(**kwargs)`) and dict literals. Python 3.9+ also supports `a | b` for the same effect (and `a |= b` for in-place merge).
pythonconfig = {**defaults, **user_overrides} def f(**kw): ...; f(**{'a': 1, 'b': 2})Output Idle -
Q178
How do you swap two variables in Python?
Easy▼ Show answer ▲ Hide answer
`a, b = b, a` — Python builds a tuple on the right and unpacks it on the left, atomically. No temp variable needed. Works for any number of names: `a, b, c = c, a, b`.
-
Q179
How do you check if an object has an attribute or a key?
Easy▼ Show answer ▲ Hide answer
Attribute: `hasattr(obj, 'name')` or EAFP via `try: obj.name ... except AttributeError`. Dict key: `key in d` (O(1)). Avoid `d.has_key()` — removed in Python 3.
-
Q180
Reverse a list in 3 different ways.
Easy▼ Show answer ▲ Hide answer
(1) `list[::-1]` — slice, returns a new list. (2) `list.reverse()` — in place, returns None. (3) `reversed(list)` — iterator (memory-efficient, can be wrapped in `list(...)`).
pythonnums = [1, 2, 3] nums[::-1] # [3, 2, 1] (new list) nums.reverse() # mutates in place list(reversed(nums)) # iterator wrappedOutput Idle -
Q181
Reverse a string in Python.
Easy▼ Show answer ▲ Hide answer
Strings are immutable — easiest is `s[::-1]`. Other options: `''.join(reversed(s))`, or recursion for fun (impractical due to recursion limit).
python'hello'[::-1] # 'olleh' ''.join(reversed('hello')) # 'olleh'Output Idle -
Q182
Remove duplicates from a list while preserving order.
Medium▼ Show answer ▲ Hide answer
Use `dict.fromkeys` — dicts have preserved insertion order since 3.7 and ignore duplicate keys. Faster and clearer than manual `seen` set.
pythonlist(dict.fromkeys([3, 1, 2, 3, 1, 4])) # [3, 1, 2, 4]Output Idle -
Q183
How do you check if a string is a palindrome?
Easy▼ Show answer ▲ Hide answer
Compare the string to its reverse. For 'real-world' palindromes (ignoring case, punctuation), filter first.
pythondef is_palindrome(s): s = ''.join(c.lower() for c in s if c.isalnum()) return s == s[::-1] is_palindrome('A man, a plan, a canal: Panama') # TrueOutput Idle -
Q184
How do you flatten a nested list of arbitrary depth?
Medium▼ Show answer ▲ Hide answer
Recursive generator with `yield from`. For one level only, `itertools.chain.from_iterable` is faster.
pythondef flatten(xs): for x in xs: if isinstance(x, list): yield from flatten(x) else: yield x list(flatten([1, [2, [3, [4]], 5]])) # [1, 2, 3, 4, 5]Output Idle -
Q185
Find the most common element in a list.
Easy▼ Show answer ▲ Hide answer
`Counter(lst).most_common(1)[0]` — returns a (value, count) tuple. For just the value: `Counter(lst).most_common(1)[0][0]` or `max(set(lst), key=lst.count)`.
pythonfrom collections import Counter Counter([1, 2, 2, 3, 3, 3]).most_common(1) # [(3, 3)]Output Idle -
Q186
Transpose a 2D list (matrix).
Easy▼ Show answer ▲ Hide answer
`list(zip(*matrix))` — unpacks rows as arguments to `zip`, which pairs by column. Each tuple is a column; wrap in `map(list, ...)` if you need lists.
pythonm = [[1, 2, 3], [4, 5, 6]] list(zip(*m)) # [(1, 4), (2, 5), (3, 6)] [list(c) for c in zip(*m)]Output Idle -
Q187
Trace the output: `(0, 1, 2, 3, (4, 5, 6), 7, 8)[::3]`
Medium▼ Show answer ▲ Hide answer
`[::3]` walks every 3rd element starting at index 0. Indices selected: 0, 3, 6 → values `0`, `3`, `8`. Result: `(0, 3, 8)`. Note that the inner tuple at index 4 is skipped entirely.
python(0, 1, 2, 3, (4, 5, 6), 7, 8)[::3] # (0, 3, 8)Output Idle -
Q188
Trace the output: `a = [[]] * 5; a[0].append(1)`
Medium▼ Show answer ▲ Hide answer
`[[]] * 5` creates a list of FIVE references to the SAME inner list — so mutating one mutates all. After `a[0].append(1)`, `a` is `[[1], [1], [1], [1], [1]]`. To get independent inner lists, use a comprehension: `[[] for _ in range(5)]`.
pythona = [[]] * 5 a[0].append(1) a # [[1], [1], [1], [1], [1]] b = [[] for _ in range(5)] b[0].append(1) b # [[1], [], [], [], []]Output Idle -
Q189
Implement Fibonacci as a generator.
Easy▼ Show answer ▲ Hide answer
A generator yields each value lazily, using O(1) memory regardless of how many you produce.
pythondef fib(): a, b = 0, 1 while True: yield a a, b = b, a + b from itertools import islice list(islice(fib(), 10)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]Output Idle -
Q190
Write a decorator that times how long a function runs.
Easy▼ Show answer ▲ Hide answer
Standard pattern: wrap with start/end timestamps and log the diff. Use `time.perf_counter()` for monotonic high-resolution timing.
pythonfrom functools import wraps import time def timed(fn): @wraps(fn) def wrap(*a, **kw): t0 = time.perf_counter() try: return fn(*a, **kw) finally: print(f'{fn.__name__} took {time.perf_counter() - t0:.4f}s') return wrapOutput Idle -
Q191
Implement a Singleton class.
Medium▼ Show answer ▲ Hide answer
Override `__new__` to cache the single instance. Beware: singletons are global state and complicate testing — a module already gives you the same thing.
pythonclass Singleton: _instance = None def __new__(cls, *a, **kw): if cls._instance is None: cls._instance = super().__new__(cls) return cls._instance Singleton() is Singleton() # TrueOutput Idle -
Q192
Group strings by length (or any key).
Easy▼ Show answer ▲ Hide answer
Use `collections.defaultdict(list)` and append to the bucket for each item's key.
pythonfrom collections import defaultdict words = ['hi', 'cat', 'dog', 'me', 'tree'] groups = defaultdict(list) for w in words: groups[len(w)].append(w) dict(groups) # {2: ['hi','me'], 3: ['cat','dog'], 4: ['tree']}Output Idle -
Q193
Count word frequencies in a sentence.
Easy▼ Show answer ▲ Hide answer
`Counter` over the lowercased split — one-liner.
pythonfrom collections import Counter text = 'the cat the dog the cat' Counter(text.lower().split()).most_common(2) # [('the', 3), ('cat', 2)]Output Idle -
Q194
Sort a list of dicts by a key.
Easy▼ Show answer ▲ Hide answer
`sorted(items, key=lambda d: d['age'])`. For multi-key, return a tuple: `key=lambda d: (d['team'], -d['score'])`. `operator.itemgetter` is faster than a lambda.
pythonfrom operator import itemgetter users = [{'name': 'B', 'age': 30}, {'name': 'A', 'age': 25}] sorted(users, key=itemgetter('age'))Output Idle -
Q195
Read a JSON file and pretty-print it.
Easy▼ Show answer ▲ Hide answer
Load with `json.load`, dump with `indent=2` and `sort_keys=True`. Always pass `ensure_ascii=False` if you want non-ASCII characters preserved instead of escaped.
pythonimport json with open('data.json', encoding='utf-8') as f: data = json.load(f) print(json.dumps(data, indent=2, sort_keys=True, ensure_ascii=False))Output Idle
No theory questions match those filters. Try clearing the search or changing topic.