Course

Python — The Complete Course

From language fundamentals to concurrency, the standard library, and interviewing in Python.

Language course · 7 units

Unit 1: Unit 1 — Syntax, Types & Built-ins

Python is dynamically typed with strong typing; the interpreter, not the compiler, enforces types. Know the built-in sequence/set/mapping types, comprehensions, and the fact that everything is an object (even functions).

Teach: What makes Python code idiomatic

Idiomatic Python reads like prose. Prefer a list comprehension to append-loops, use enumerate() when you need indices, and reach for dict.get(k, default) / collections.Counter for counting. Worked: count word frequencies of ['a','b','a'] -> Counter -> {'a':2,'b':1}. O(n) build. Note: strings are immutable; joining is linear.

Exercises

Practice problems

Unit 2: Unit 2 — OOP: classes, dataclasses, dunder methods

Python OOP is protocol-based: behaviour comes from dunder methods (__eq__, __lt__, __hash__, __repr__) and classes are first-class objects. Prefer dataclasses for value objects, Protocol for duck-typed interfaces, and slotted classes for tight loops.

Teach: dataclasses + value semantics

A Point dataclass with __eq__ auto-generated means p1 == p2 compares fields. If you put a dataclass in a set/dict you must ALSO define __hash__ — which dataclass(frozen=True) gives you automatically. Frozen = immutable = safely hashable.

Exercises

Practice problems

Unit 3: Unit 3 — Concurrency: threads, asyncio, GIL reality

Python threads are fine for I/O (GIL releases around blocking calls) but not for CPU-bound work — use multiprocessing or asyncio for I/O-bound. Know: GIL, ThreadPoolExecutor, asyncio.run/await, and the awaitable primitive. Interviewers probe 'which tool for which bound'.

Teach: The GIL and the I/O vs CPU distinction

The GIL lets only one thread run Python bytecode at a time, but it is released during blocking I/O (socket, file) — so many threads genuinely parallelize I/O. For CPU-bound Python, threads give no speedup; use multiprocessing or a C extension. State this distinction explicitly: 'this is I/O-bound so threads help'.

Exercises

Practice problems

Unit 4: Unit 4 — Standard library & Python interviewing

Interviewers love to see you reach for the right stdlib: collections (Counter, defaultdict, deque, OrderedDict), heapq, bisect, itertools, functools (lru_cache), re. Knowing these signals real-world fluency and keeps your solutions compact and correct.

Teach: engineering speed for the standard patterns

Top-k in one line: heapq.nlargest(k, stream). Sliding-window max: a deque holding candidate indices, O(n). Memoized recursion: @lru_cache(None) on a pure function. Knowing these is the difference between 'works' and 'elegant, O(n), no bugs'.

Exercises

Practice problems

Unit 5: Unit 5 — Data structures & the collections module

Interview Python leans on the standard data structures and the collections module. Know when each shines: list (ordered, O(1) append), dict (O(1) lookup), set (O(1) membership, dedupe), deque (O(1) both ends), heapq (min-heap ops), bisect (sorted insertion/search), defaultdict/Counter (counting), OrderedDict (insertion order + move_to_end). The right container often turns an O(n^2) solution into O(n log n) or O(n).

Teach: choosing the right container

Ask: what is the dominant operation? If you need the smallest/largest repeatedly → heap. If you need O(1) both-ends append/pop → deque. If you need a running sorted order with insert/search → bisect on a list (or a heap if you only need the extreme). If you group items by a key → defaultdict. If you count occurrences → Counter. Worked example — Top K Frequent Elements: count with Counter (O(n)), then heapq.nlargest(k, counts.items(), key=itemgetter(1)) → O(n log k). State the space cost of each container.

Exercises

Practice problems

Unit 6: Unit 6 — Functional Python: generators, itertools, functools

Python is multi-paradigm; interviewers probe whether you can write clear functional-style code. Generators (yield) give lazy, memory-efficient iteration and are the backbone of pipelines. itertools (chain, product, combinations, permutations, groupby, accumulate) and functools (reduce, lru_cache, partial, cmp_to_key) replace hand-rolled loops. Know when a generator beats a list: when you stream, when you compose, when memory matters.

Teach: generators are lazy pipelines

A generator function with yield produces values on demand — it does not build the whole sequence. This is O(1) memory for streaming. Compose generators: (x*x for x in range(n)) is lazy; [x*x for x in range(n)] is eager. Worked example — first n primes: a generator yields primes forever; itertools.islice takes the first n. Memory stays O(primes so far) instead of O(whole sieve). Mention lru_cache for memoized recursion and cmp_to_key to sort with a comparator.

Exercises

Practice problems

Unit 7: Unit 7 — Performance: profiling, __slots__, C extensions & numpy

Staff-level Python means reasoning about where time and memory actually go. Profile before optimizing: cProfile / timeit identify the hot path; the GIL limits CPU-bound threads (use multiprocessing or a C extension); __slots__ cuts per-instance memory; numpy vectorizes numeric loops; C extensions (ctypes/cffi/Cython) escape Python overhead for the critical inner loop. Always quantify: measure before and after, and state the complexity.

Teach: measure first, optimize the hot loop

Never guess the bottleneck. Run cProfile on the workload, find the function with the most cumulative time, and optimize THAT. Common wins: replace a Python loop with a numpy op or a comprehension; move the inner loop into a C extension; cache repeated work; reduce allocation. For CPU-bound parallelism the GIL blocks threads — use multiprocessing or a C extension. State the before/after numbers.

Exercises

Practice problems

Open this course in Dojo