Python — Core Idioms
Comprehensions, generators, context managers, decorators.
Comprehensions
# List, set, dict, generator comprehensions
squares = [x*x for x in range(10) if x % 2 == 0]
uniq = {w.lower() for w in words}
by_id = {u.id: u for u in users}
lazy = (x*x for x in big) # generator, O(1) memory
flat = [c for row in matrix for c in row] # flatten (nested for)
mat3x3 = [[0]*3 for _ in range(3)] # correct; [[0]*3]*3 aliases!
Generators & yield
# Lazy Fibonacci
def fib():
a, b = 0, 1
while True: yield a; a, b = b, a+b
from itertools import islice
print(list(islice(fib(), 10))) # first 10
# yield from — delegate
def chain_all(*its):
for it in its: yield from it
Generators hold state as a frozen call frame; resume on next().
Context Managers
# with-block pattern for setup/teardown
from contextlib import contextmanager
@contextmanager
def timer(label):
import time
t0 = time.perf_counter()
try: yield
finally: print(f"{label}: {(time.perf_counter()-t0)*1000:.2f}ms")
with timer("work"): do_thing()
# Multiple contexts in one with
with open("a") as f, open("b","w") as g: g.write(f.read())
Decorators
from functools import wraps
def retry(n=3):
def deco(fn):
@wraps(fn)
def wrapped(*a, **kw):
for i in range(n):
try: return fn(*a, **kw)
except Exception:
if i == n-1: raise
return wrapped
return deco
@retry(n=5)
def flaky(): ...
Unpacking, * / **
a, *mid, z = [1,2,3,4,5] # a=1, mid=[2,3,4], z=5
head, *rest = ["a","b","c"]
def f(**kw): pass
f(**{"x":1, "y":2}) # forward kwargs
merged = {**a, **b} # dict merge (Python 3.9+: a | b)
combined = [*list_a, *list_b] # list concat
Slicing
s[::-1] # reverse
s[::2] # every second
s[:-1] # drop last
a, b = s[:n//2], s[n//2:]
mat[::-1] # reverse rows
[row[:3] for row in mat] # first 3 cols
enumerate, zip, any, all
for i, x in enumerate(xs, start=1): ...
for a, b in zip(xs, ys): ... # truncates to shorter
# Python 3.10+: zip(..., strict=True) # raises on unequal length
all(x > 0 for x in xs) # short-circuit
any(p(x) for x in xs)
Walrus :=
while (chunk := f.read(8192)): process(chunk)
if (m := re.search(r"\d+", s)): print(m.group())
List Op Complexity
Python — stdlib Essentials
Batteries interviewers expect you to use.
collections
from collections import deque, defaultdict, Counter, OrderedDict, namedtuple
q = deque([1,2,3]); q.appendleft(0); q.popleft() # O(1) both ends
graph = defaultdict(list)
graph[1].append(2) # no KeyError
c = Counter("abracadabra")
c.most_common(2) # [('a', 5), ('b', 2)]
Point = namedtuple("Point", "x y")
p = Point(1, 2); p.x, p.y
# dataclass is usually better for new code
heapq (min-heap)
import heapq
h = []
heapq.heappush(h, 3); heapq.heappush(h, 1)
heapq.heappop(h) # 1
# heapify in place O(n)
arr = [5,2,8,1]; heapq.heapify(arr)
heapq.nlargest(3, arr); heapq.nsmallest(3, arr)
# max-heap trick: negate values
heapq.heappush(h, -value); -heapq.heappop(h)
bisect
import bisect
a = [1,2,4,4,5]
bisect.bisect_left(a, 4) # 2 (first ≥ 4)
bisect.bisect_right(a, 4) # 4 (first > 4)
bisect.insort(a, 3) # keeps sorted; O(n) due to shift
insort is O(n) — shift cost dominates.
itertools
from itertools import combinations, permutations, product, chain, accumulate, groupby, islice, cycle, repeat
list(combinations([1,2,3], 2)) # (1,2),(1,3),(2,3)
list(permutations([1,2,3], 2)) # (1,2),(1,3),(2,1),(2,3),(3,1),(3,2)
list(product([0,1], repeat=3)) # all 3-bit tuples
list(chain([1,2],[3,4])) # flatten
list(accumulate([1,2,3,4])) # prefix sum [1,3,6,10]
# sorted+groupby to bucket
for k, g in groupby(sorted(items, key=key), key=key):
print(k, list(g))
functools
from functools import lru_cache, cache, reduce, partial
@lru_cache(maxsize=None)
def fib(n): return n if n < 2 else fib(n-1)+fib(n-2)
@cache # Python 3.9+: alias for lru_cache(maxsize=None)
def f(x): ...
reduce(lambda a,b: a*b, nums, 1)
p = partial(int, base=16); p("ff") # 255
dataclasses & typing
from dataclasses import dataclass, field
from typing import Optional
@dataclass(frozen=True, slots=True) # Python 3.10+ for slots
class User:
id: int
name: str
tags: list[str] = field(default_factory=list) # never default = []!
# typing
def get(x: int | None) -> str: # Python 3.10+ union syntax
return str(x) if x is not None else ""
Python — Concurrency & Internals
GIL, asyncio, the pitfalls interviewers ask about.
The GIL
CPython's Global Interpreter Lock serializes bytecode execution — only one thread runs Python bytecode at a time. Threads DO help for I/O (the lock releases on I/O syscalls); they do NOT help CPU-bound work. For CPU-bound: multiprocessing, concurrent.futures.ProcessPoolExecutor, or C-extensions releasing the GIL (numpy). Python 3.13+ has optional free-threaded "no-GIL" build; not yet default.
asyncio
import asyncio, aiohttp
async def fetch(session, url):
async with session.get(url) as r:
return await r.text()
async def main(urls):
async with aiohttp.ClientSession() as s:
return await asyncio.gather(*(fetch(s, u) for u in urls))
asyncio.run(main(urls))
Common Pitfalls
- Mutable default arg:
def f(x=[])— default list created once, shared across calls. Usex=Nonethen assign inside. - Late binding in closures:
[lambda: i for i in range(3)]all return 2. Fix:lambda i=i: i. isvs==:is= identity,=== equality.None,True,Falseuseis; everything else==.- Dict iteration order: insertion-ordered since 3.7 (CPython 3.6). OrderedDict still useful for
move_to_end+ equality semantics. - Shallow copy:
a = b[:]orlist(b)copies list but not nested objects.copy.deepcopyfor full.
Java — Collections
Which impl, which complexity.
Complexity Reference
*LinkedList.remove given a node ref is O(1); by index is O(n).
HashMap Internals
Backing array of buckets; each bucket is a linked list. Java 8+: when a bucket grows past 8 entries AND table > 64, it converts to a red-black tree, improving worst-case from O(n) to O(log n). Default load factor 0.75 → resize (× 2) when size > capacity × 0.75.
ConcurrentHashMap
var m = new ConcurrentHashMap<String, AtomicInteger>();
m.computeIfAbsent("k", k -> new AtomicInteger()).incrementAndGet();
m.merge("k", 1, Integer::sum);
Lock-striped; iteration is weakly consistent (no ConcurrentModificationException). Prefer over Collections.synchronizedMap.
PriorityQueue
var pq = new PriorityQueue<int[]>((a,b) -> a[0] - b[0]);
pq.offer(new int[]{3, 'x'});
pq.peek(); pq.poll();
// max-heap: reverse comparator
var maxPq = new PriorityQueue<Integer>(Comparator.reverseOrder());
Java — Streams & Concurrency
Declarative pipelines; thread pools; CompletableFuture.
Stream API
import static java.util.stream.Collectors.*;
var byDept = employees.stream()
.filter(e -> e.salary() > 50_000)
.collect(groupingBy(Employee::dept, counting()));
// reduce
int sum = nums.stream().mapToInt(Integer::intValue).sum();
// parallel — only for independent, CPU-bound, large work
int total = bigList.parallelStream().mapToInt(this::score).sum();
Optional
Optional<User> u = repo.find(id);
u.map(User::email).orElse("unknown");
// Don't do .get() without isPresent — misses the point.
// Don't use Optional for fields/params — only return types.
ExecutorService
var pool = Executors.newFixedThreadPool(8);
Future<Integer> f = pool.submit(() -> heavy());
int r = f.get();
pool.shutdown();
// Java 21 virtual threads
try (var es = Executors.newVirtualThreadPerTaskExecutor()) {
es.submit(task);
}
CompletableFuture
CompletableFuture.supplyAsync(this::loadUser)
.thenApply(User::email)
.thenCompose(this::sendWelcome)
.exceptionally(ex -> { log(ex); return null; });
var all = CompletableFuture.allOf(f1, f2, f3);
var any = CompletableFuture.anyOf(f1, f2);
Locks & Concurrency Utilities
synchronized+Object.wait/notify— low-level, avoid for new code.ReentrantLock,ReadWriteLock— fairness options, tryLock with timeout.Semaphore,CountDownLatch,CyclicBarrier,Phaser.AtomicInteger,LongAdder— lock-free counters.StampedLock— optimistic read + write locks.
Java — Interview Patterns
Comparators, resources, records, switch.
Comparator idioms
Comparator<Person> byAgeThenName =
Comparator.comparingInt(Person::age).thenComparing(Person::name);
// reverse
list.sort(Comparator.reverseOrder());
list.sort(byAgeThenName.reversed());
// nulls last
Comparator.nullsLast(Comparator.naturalOrder());
try-with-resources
try (var in = Files.newBufferedReader(p);
var out = Files.newBufferedWriter(dst)) {
out.write(in.readLine());
} // closed in reverse order, exceptions suppressed-attached
Records (Java 16+)
public record Point(int x, int y) {
public Point { if (x < 0 || y < 0) throw new IllegalArgumentException(); } // compact ctor
public int sum() { return x + y; }
}
// getters, equals, hashCode, toString auto-generated; final + immutable
Pattern Matching in switch (Java 21)
String describe(Object o) {
return switch (o) {
case Integer i when i < 0 -> "neg int";
case Integer i -> "int " + i;
case String s -> "string len=" + s.length();
case null -> "null";
default -> "other";
};
}
C++ — STL & Modern
Containers, algorithms, move semantics, smart pointers.
Container Complexity
* With iterator in hand.
<algorithm>
#include <algorithm>
#include <numeric>
std::sort(v.begin(), v.end());
std::sort(v.begin(), v.end(), std::greater<>{});
auto it = std::lower_bound(v.begin(), v.end(), x);
std::accumulate(v.begin(), v.end(), 0LL);
int cnt = std::count_if(v.begin(), v.end(), [](int x){ return x>0; });
std::unique(v.begin(), v.end()); // requires sorted first; returns new end
v.erase(std::unique(v.begin(), v.end()), v.end()); // erase-remove idiom
Move Semantics & RAII
std::vector<std::string> make() {
std::vector<std::string> v;
v.push_back(std::string(1000, 'x')); // move from temporary
return v; // NRVO / move, not copy
}
class Buf {
std::vector<char> data;
public:
Buf(Buf&& o) noexcept : data(std::move(o.data)) {} // move ctor
Buf& operator=(Buf&& o) noexcept { data = std::move(o.data); return *this; }
};
RAII: resource lifetime = object lifetime. Acquire in ctor, release in dtor. No manual new/delete in modern C++.
Smart Pointers
auto p = std::make_unique<Widget>(arg); // sole owner
auto s = std::make_shared<Widget>(arg); // shared owner, atomic refcount
std::weak_ptr<Widget> w = s; // non-owning observer
Go — For Interviews
Slices, maps, goroutines, channels, context.
Slices (len vs cap)
s := make([]int, 0, 10) // len=0 cap=10
s = append(s, 1, 2, 3) // len=3 cap=10, no realloc
fmt.Println(len(s), cap(s))
// slicing shares backing array
a := []int{1,2,3,4,5}
b := a[1:3] // b shares storage
b[0] = 99 // mutates a[1]!
// defensive copy
c := make([]int, len(b)); copy(c, b)
Maps
m := map[string]int{}
m["a"] = 1
v, ok := m["a"] // ok=false if missing
delete(m, "a")
// iteration order is randomized by design
Goroutines & Channels
ch := make(chan int, 3) // buffered
go func() {
for i := 0; i < 3; i++ { ch <- i }
close(ch)
}()
for v := range ch { fmt.Println(v) }
// select — first ready wins
select {
case v := <-ch: handle(v)
case <-time.After(1*time.Second): timeout()
case <-ctx.Done(): return ctx.Err()
}
sync
var (
mu sync.Mutex
wg sync.WaitGroup
)
for _, url := range urls {
wg.Add(1)
go func(u string) {
defer wg.Done()
r, err := http.Get(u)
mu.Lock(); results[u] = r; mu.Unlock()
}(url)
}
wg.Wait()
context.Context
ctx, cancel := context.WithTimeout(parent, 2*time.Second)
defer cancel()
req, _ := http.NewRequestWithContext(ctx, "GET", url, nil)
Every blocking or RPC call takes a context.Context as its first arg. Cancellation propagates automatically.
Error handling
if err := doX(); err != nil {
return fmt.Errorf("do X: %w", err) // wrap
}
var nf *NotFound
if errors.As(err, &nf) { /* custom type */ }
if errors.Is(err, io.EOF) { /* sentinel */ }
TypeScript — Essentials
Types, generics, narrowing, strict mode.
Types vs Interfaces
type User = { id: number; name: string };
interface User2 { id: number; name: string }
// interface: declaration merging, extends via `extends`
// type: unions, intersections, mapped, conditional
type Role = "admin" | "user" | "guest";
type AdminUser = User & { role: "admin" };
Generics
function first<T>(xs: T[]): T | undefined { return xs[0]; }
// constraints
function getKey<T, K extends keyof T>(obj: T, k: K): T[K] { return obj[k]; }
class Cache<T> {
private map = new Map<string, T>();
get(k: string): T | undefined { return this.map.get(k); }
}
Utility Types
type U = Partial<User>; // all optional
type U2 = Required<U>;
type U3 = Readonly<User>;
type P = Pick<User, "id" | "name">;
type O = Omit<User, "id">;
type R = Record<Role, User[]>; // map Role to User[]
type T = ReturnType<typeof fn>;
type A = Awaited<Promise<string>>; // string
Discriminated Unions + Narrowing
type Shape =
| { kind: "circle"; r: number }
| { kind: "square"; side: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.r ** 2; // s narrowed
case "square": return s.side ** 2;
}
}
// Type guard fn
function isErr(x: unknown): x is Error {
return x instanceof Error;
}
Strict Mode
Enable "strict": true in tsconfig. This turns on noImplicitAny, strictNullChecks, strictFunctionTypes, and more. any defeats the type system — prefer unknown + narrowing.
Language-agnostic Interview Habits
How to write good code under time pressure.
Naming
- Full words > abbreviations.
customer_count>custCnt. - Boolean names start with
is,has,can. - Collections in plural. Loop variables short:
i, j, kfor indices, single noun for items. - Match the domain, not the implementation.
orders>order_list.
Early Return & Guard Clauses
# prefer this
def process(req):
if not req.valid: return Err(422)
if not req.user.active: return Err(403)
# happy path, flat
Deep nesting hides the happy path. Flatten with early return.
Error Handling
- Handle errors at the right boundary. Don't catch-and-swallow deep inside.
- Attach context when wrapping:
fmt.Errorf("load user %d: %w", id, err). - Distinguish programmer errors (panics, assertions) from user-visible errors.
Test Skeleton
In interview, leave an empty test block — shows you think test-first. e.g. "# test: empty list returns []; list of dups returns 1; mixed returns sorted unique".
Communication
- Talk while you think: state your current approach before coding.
- Confirm constraints before optimizing ("is n ≤ 10⁵? Then O(n log n) is fine").
- When stuck: enumerate what you know, what you don't. Ask for a hint if truly stuck — that's fine.
- Before declaring done: trace through one case; state time & space.