Python Interview Tricks: 25 Built-ins and Patterns That Make Your Code 3x Faster to Write
Python's standard library is a secret weapon in interviews. While others write 10-line implementations, you can solve the same problem in 2 lines β and spend the remaining time thinking about edge cases. Here are the 25 tricks that matter most.
Python is the dominant language in Indian tech interviews for good reason: it lets you express ideas quickly, its built-ins cover most common data structure operations, and readable code signals professional instincts.
But most students use Python like they're writing C β manual loops, manual implementations, no standard library. This guide shows you the tools that let experienced Python developers solve problems in half the time.
1. collections.defaultdict β Never Check Key Existence Again
from collections import defaultdict
# Without defaultdict β clunky
freq = {}
for char in "interview":
if char not in freq:
freq[char] = 0
freq[char] += 1
# With defaultdict β clean
freq = defaultdict(int)
for char in "interview":
freq[char] += 1
# Graph building
graph = defaultdict(list)
for u, v in edges:
graph[u].append(v)
graph[v].append(u)
defaultdict(int) defaults to 0. defaultdict(list) defaults to []. defaultdict(set) defaults to set().
2. collections.Counter β Frequency Counting in One Line
from collections import Counter
# Character frequency
freq = Counter("aabbbcccc")
# Counter({'c': 4, 'b': 3, 'a': 2})
# Most common k elements
top3 = freq.most_common(3) # [('c', 4), ('b', 3), ('a', 2)]
# Check if anagram
def is_anagram(s, t):
return Counter(s) == Counter(t)
# Subtract counters
c1 = Counter("aab")
c2 = Counter("ab")
print(c1 - c2) # Counter({'a': 1})
# Total elements
total = sum(freq.values())
# Frequency of frequencies (useful for grouping)
word_freq = Counter("apple banana apple cherry banana apple".split())
# Counter({'apple': 3, 'banana': 2, 'cherry': 1})
3. collections.deque β O(1) Operations on Both Ends
from collections import deque
# BFS queue
queue = deque([start_node])
while queue:
node = queue.popleft() # O(1) β use list.pop(0) and it's O(n)
for neighbor in graph[node]:
queue.append(neighbor)
# Sliding window of fixed size
window = deque(maxlen=k) # auto-removes oldest when full
for num in nums:
window.append(num)
if len(window) == k:
process(window)
# Deque as double-ended queue
d = deque([1, 2, 3])
d.appendleft(0) # [0, 1, 2, 3]
d.popleft() # 0
d.append(4) # [1, 2, 3, 4]
d.pop() # 4
d.rotate(1) # [3, 1, 2] (rotate right by 1)
4. heapq β Priority Queue Without a Class
import heapq
# Min-heap operations
heap = []
heapq.heappush(heap, 3)
heapq.heappush(heap, 1)
heapq.heappush(heap, 2)
heapq.heappop(heap) # 1 (smallest)
# Convert list to heap in O(n)
arr = [3, 1, 4, 1, 5, 9, 2, 6]
heapq.heapify(arr)
# Max-heap: negate values
heapq.heappush(heap, -value)
max_val = -heapq.heappop(heap)
# Push tuples for custom sort (sorts by first element)
heapq.heappush(heap, (distance, node))
# K smallest / K largest
heapq.nsmallest(3, arr) # [1, 1, 2]
heapq.nlargest(3, arr) # [9, 6, 5]
5. bisect β Binary Search Without Writing It
import bisect
arr = [1, 3, 5, 7, 9]
# Find insertion point (maintains sorted order)
bisect.bisect_left(arr, 5) # 2 (leftmost position for 5)
bisect.bisect_right(arr, 5) # 3 (rightmost position for 5)
# Count occurrences in sorted array
def count_occurrences(arr, target):
return bisect.bisect_right(arr, target) - bisect.bisect_left(arr, target)
# Insert while maintaining sorted order
bisect.insort(arr, 4) # arr = [1, 3, 4, 5, 7, 9]
# Find first element >= target
idx = bisect.bisect_left(arr, target)
# arr[idx] is the first element >= target (if idx < len(arr))
6. Enumerate and Zip β Clean Loop Control
# enumerate: index + value without manual counter
for i, val in enumerate(arr):
print(i, val)
# Start index at 1
for i, val in enumerate(arr, start=1):
print(i, val)
# zip: iterate two lists in parallel
for a, b in zip(list1, list2):
print(a, b)
# zip stops at shortest β use zip_longest for same length
from itertools import zip_longest
for a, b in zip_longest(list1, list2, fillvalue=0):
print(a, b)
# Unzip a list of pairs
pairs = [(1, 'a'), (2, 'b'), (3, 'c')]
numbers, letters = zip(*pairs)
# numbers = (1, 2, 3), letters = ('a', 'b', 'c')
7. List Comprehensions and Generator Expressions
# Instead of:
result = []
for x in range(10):
if x % 2 == 0:
result.append(x ** 2)
# Write:
result = [x**2 for x in range(10) if x % 2 == 0]
# Nested: flatten 2D matrix
flat = [cell for row in matrix for cell in row]
# Dictionary comprehension
word_len = {word: len(word) for word in words}
# Set comprehension: unique characters
unique_chars = {c for c in "mississippi"}
# Generator (memory-efficient for large data)
total = sum(x**2 for x in range(10**6)) # never builds the full list
8. functools.lru_cache β Memoisation in One Line
from functools import lru_cache
# Turn any recursive function into memoised DP
@lru_cache(maxsize=None)
def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
@lru_cache(maxsize=None)
def coin_change(coins, amount): # tuples are hashable; lists are not
if amount == 0:
return 0
if amount < 0:
return float('inf')
return 1 + min(coin_change(coins, amount - c) for c in coins)
# Call: coin_change(tuple([1, 2, 5]), 11)
# Clear cache between test cases in competitive programming
fib.cache_clear()
9. String Methods That Save Lines
# Check character types
"abc123".isalpha() # False (has digits)
"abc".isalpha() # True
"123".isdigit() # True
"abc123".isalnum() # True (all alphanumeric)
# Useful in palindrome check
def is_palindrome(s):
clean = [c.lower() for c in s if c.isalnum()]
return clean == clean[::-1]
# Split and join
words = "hello world".split() # ['hello', 'world']
joined = " ".join(words) # 'hello world'
",".join(["a", "b", "c"]) # 'a,b,c'
# Strip whitespace/characters
" hello ".strip() # 'hello'
"###hello###".strip('#') # 'hello'
# Replace
"aabba".replace("a", "x") # 'xxbbx'
# Count occurrences
"mississippi".count("ss") # 2
10. Sorting With Custom Keys
# Sort by length, then alphabetically
words = ["banana", "fig", "apple", "kiwi"]
words.sort(key=lambda w: (len(w), w))
# ['fig', 'kiwi', 'apple', 'banana']
# Sort by second element of tuple
pairs = [(1, 3), (2, 1), (3, 2)]
pairs.sort(key=lambda p: p[1])
# [(2, 1), (3, 2), (1, 3)]
# Sort descending
arr.sort(reverse=True)
# Sort by multiple criteria: primary ascending, secondary descending
data.sort(key=lambda x: (x.age, -x.score))
# Stable: Python's sort preserves relative order for equal keys
# This lets you sort by one key, then another, safely
# For custom objects, use operator.attrgetter (faster than lambda)
from operator import attrgetter
students.sort(key=attrgetter('gpa'))
11. Two-Liner Interview Solutions
# Valid parentheses
def is_valid(s):
stack = []
mapping = {')': '(', '}': '{', ']': '['}
return all((stack.append(c) or True) if c in '([{' else
stack and stack.pop() == mapping[c]
for c in s) and not stack
# Flatten nested list (itertools)
from itertools import chain
flat = list(chain.from_iterable(nested_list))
# Running sum (prefix sums)
from itertools import accumulate
prefix = list(accumulate(arr)) # [a0, a0+a1, a0+a1+a2, ...]
# Running product
from itertools import accumulate
from operator import mul
prefix_prod = list(accumulate(arr, mul))
# Group consecutive elements
from itertools import groupby
for key, group in groupby([1,1,2,3,3,3]):
print(key, list(group))
# 1 [1, 1]
# 2 [2]
# 3 [3, 3, 3]
12. Integer Tricks
# Integer division and modulo
17 // 5 # 3 (floor division)
17 % 5 # 2 (modulo)
divmod(17, 5) # (3, 2) β both at once
# Infinity (for initialisation)
INF = float('inf')
NEG_INF = float('-inf')
min(INF, 5) # 5
# Bit operations
n & 1 # check if odd (1) or even (0)
n >> 1 # divide by 2
n << 1 # multiply by 2
n & (n - 1) # clear lowest set bit (0 if n is power of 2)
bin(n).count('1') # count set bits (Hamming weight)
# Swap without temp variable
a, b = b, a
# Multiple assignment
x = y = z = 0
13. The any() and all() Shortcuts
# Instead of a loop with a flag:
def has_duplicate(arr):
return len(arr) != len(set(arr))
# Or with any():
def has_negative(arr):
return any(x < 0 for x in arr) # stops at first True
def all_positive(arr):
return all(x > 0 for x in arr) # stops at first False
# Check if matrix has any zero row
has_zero_row = any(all(cell == 0 for cell in row) for row in matrix)
14. Set Operations
a = {1, 2, 3, 4}
b = {3, 4, 5, 6}
a | b # union: {1, 2, 3, 4, 5, 6}
a & b # intersection: {3, 4}
a - b # difference: {1, 2}
a ^ b # symmetric difference: {1, 2, 5, 6}
# Membership is O(1) β use sets for visited tracking
visited = set()
visited.add(node)
if node in visited: # O(1)
continue
15. Common Patterns Made Concise
# Reverse a string/list
"hello"[::-1] # "olleh"
[1,2,3,4][::-1] # [4,3,2,1]
# Max/min with index
max(enumerate(arr), key=lambda x: x[1]) # (index, max_value)
# Transpose a matrix
transposed = list(zip(*matrix))
# Remove duplicates while preserving order
seen = set()
unique = [x for x in arr if not (x in seen or seen.add(x))]
# Flatten + deduplicate
all_unique = set(chain.from_iterable(nested_list))
# Dictionary from two lists
d = dict(zip(keys, values))
# Frequency dict from list
freq = {x: arr.count(x) for x in set(arr)} # O(nΒ²) β use Counter instead
When NOT to Use These Tricks
These built-ins are production-quality β but in interviews, clarity matters. If an interviewer asks you to implement binary search, write the binary search. Using bisect when explicitly asked to implement it signals you missed the point.
Use built-ins when:
- The problem is about the algorithm, not the data structure
- The interviewer says "you can use standard library functions"
- It makes your code significantly cleaner
Implement manually when:
- Asked explicitly to implement a data structure
- The implementation is the point (implement LRU cache, not
@lru_cache)
This week: Pick 5 problems from Practice you've previously solved. Re-solve each one using at least 2 Python tricks from this list. Notice how much shorter and clearer the code becomes.
Ready to practice what you just learned?
Apply these concepts with AI-powered tools built for CS students.