16 progressive lessons · 40 graded exercises with solutions · 3 build-it-yourself projects
Every one of the 60 programs here runs, passes ruff, and type-checks under mypy --strict.
This repository started as two files from a class — a function that counts
repeats, and one that filters a list of films by genre. They are still here,
unedited, in coursework/.
Everything else is the distance travelled from them: what those two exercises
turn into once you know about type hints, Counter, dataclasses, generators,
context managers and a test suite. The starting line is kept on purpose,
because deleting it would make the distance impossible to measure.
git clone https://github.com/limonuser/Python_educator.git
cd Python_educator
python -m pip install -e ".[dev]" # pytest, ruff, mypy
make # lint + type-check + test + run everything
make lesson-09 # run one lesson
make test # just the testsEvery file is standalone, so you never actually need make:
python lessons/09-dicts-and-sets/mappings.pyNo Python yet? See docs/SETUP.md — Linux, macOS and Windows.
Work top to bottom. Each lesson assumes the one above it and nothing else.
| # | Lesson | You will learn | Key file |
|---|---|---|---|
| 01 | Hello, World | How Python runs a file, bytecode and __pycache__, and what if __name__ == "__main__" actually prevents |
hello.py |
| 02 | Variables & Types | Names are references, not boxes; is vs ==, interning, float imprecision, unbounded int |
variables.py |
| 03 | Operators | / vs // vs %, flooring toward negative infinity, chained comparison, truthiness, the walrus |
operators.py |
| 04 | Control Flow | if/elif, match/case and its capture-vs-compare trap, when if x: is the wrong test |
control.py |
| 05 | Loops | enumerate, zip(strict=), what for/else really means, mutating a list while iterating it |
loops.py |
| 06 | Functions | The mutable default argument trap, *args/**kwargs, keyword-only params, closures and late binding |
functions.py |
| # | Lesson | You will learn | Key file |
|---|---|---|---|
| 07 | Lists & Tuples | Slicing, shallow vs deep copy and the aliasing bug it hides, sorting with key=, stability |
sequences.py |
| 08 | Strings | Immutability, why += in a loop is O(n²), f-string format specs, str vs bytes |
strings.py |
| 09 | Dicts & Sets | get/setdefault, defaultdict, Counter, hashability, O(1) vs O(n) membership measured |
mappings.py |
| 10 | Comprehensions | List/dict/set forms, nested-for ordering, generator expressions, when not to use one |
comprehensions.py |
| 11 | Iterators & Generators | The iterator protocol, yield, generator exhaustion, itertools, memory measured with getsizeof |
generators.py |
| # | Lesson | You will learn | Key file |
|---|---|---|---|
| 12 | Classes & Objects | __repr__ vs __str__, __eq__ with __hash__, @property, dataclasses, the class-attribute trap |
classes.py |
| 13 | Errors & Exceptions | What else and finally are for, why bare except is never right, raise … from, EAFP vs LBYL |
errors.py |
| 14 | Modules & Packages | A real package (textkit/), __all__, relative imports, why module code runs once, circular imports |
main.py + textkit/ |
| 15 | Files & Context Managers | with and what it guarantees, writing your own, pathlib, json, csv, always encoding="utf-8" |
fileio.py |
| 16 | Testing & Typing | pytest, raises, parametrize, fixtures; annotations aren't enforced — mypy is what enforces them |
demo.py |
Forty problems, sorted by difficulty. Every one ships with a full solution — in a separate file, so looking is a decision rather than an accident.
| Tier | Problems | Sample |
|---|---|---|
| Beginner | 15 | FizzBuzz, prime sieve, digit sum, palindrome, GCD/LCM, char frequency |
| Intermediate | 15 | Binary search, Caesar cipher, anagrams, Roman numerals, RLE, base conversion |
| Advanced | 10 | LRU cache, hash table, BST, binary heap, decorators, context managers, JSON parser |
cd practice/beginner
python 01_fizzbuzz.pyThe advanced tier is covered by the test suite:
python -m pytest tests/test_practice_advanced.py -vThree complete programs. Each one is argparse-driven, fully typed, raises its own exception types rather than leaking tracebacks, and runs a self-contained demonstration when you give it no arguments.
| Project | What it is | Lines | Pulls in |
|---|---|---|---|
| 01 — Calculator | Infix expression evaluator: tokenizer → shunting-yard → RPN. Never calls eval() |
485 | parsing, custom exceptions, argparse |
| 02 — Todo CLI | Task manager with JSON persistence, subcommands, and stable IDs | 555 | dataclasses, enums, pathlib, json |
| 03 — Word Frequency | Streaming text analyzer with a histogram and hapax count | 392 | Counter, re, generators |
python projects/01-calculator/calc.py "5 + (1 + 2) * 4 - 3" # 14
python projects/02-todo-cli/todo.py add "learn generators"
python projects/03-word-frequency/wordfreq.py book.txt -n 25Four checks, enforced by CI on Python 3.11, 3.12 and 3.13:
python -m ruff check . # lint
python -m ruff format --check . # formatting
python -m mypy . # types, --strict
python -m pytest # 292 tests- Every function is annotated.
mypy --strictpasses on all 70 modules — noAnyleaking out, no untyped defs, no silent# type: ignore. - 292 tests, all passing. Not smoke tests: the calculator's error paths, the
todo store's disk round-trip, the LRU cache's eviction order, and the JSON
parser checked against
json.loads. - Every program runs. CI executes all 60 of them and fails on a non-zero exit.
coursework/is exempt and stays that way — those two files are a record, not a target. CI still runs them, so they have to keep working.
Python_educator/
├── coursework/ the two original class exercises, unedited
├── lessons/ 16 lessons, each a folder you can cd into
│ ├── 01-hello-world/ README.md + hello.py
│ ├── ...
│ └── 14-modules-and-packages/ README.md + main.py + textkit/
├── practice/ 40 problems with full solutions
│ ├── beginner/ 15 · loops, arithmetic, strings, lists
│ ├── intermediate/ 15 · algorithms, parsing, bit work
│ └── advanced/ 10 · data structures built from scratch
├── projects/ 3 complete programs
├── tests/ 292 pytest tests over the projects and advanced tier
├── docs/ setup · cheatsheet · gotchas · style · roadmap
├── .github/workflows/ CI on 3.11 / 3.12 / 3.13
├── pyproject.toml ruff + mypy + pytest config
└── Makefile make check runs the whole bar
| Document | Contents |
|---|---|
| docs/SETUP.md | Getting Python and a virtualenv on any OS; what each tool flag buys you |
| docs/CHEATSHEET.md | f-string specs, str/list/dict/set methods, itertools, dunders, truthiness |
| docs/GOTCHAS.md | Fifteen Python traps, each with a wrong/right pair and the reason |
| docs/STYLE_GUIDE.md | The conventions every file here follows |
| docs/ROADMAP.md | Where this curriculum goes next |
- Read the lesson README first. It frames the problem before showing code.
- Type the example out. Do not copy-paste.
- Break it deliberately. Drop the
encoding=, mutate the list you are iterating, give a function a mutable default. Watch what happens — and what doesn't warn you. - Do the exercises before reading the solutions. A solution you read is worth about a tenth of one you wrote.
- Run
mypyon your own answer. It finds the bug your test forgot.
MIT — use it, fork it, teach with it.