How to Master Python
Mastery is not memorizing list.append or pasting a FastAPI snippet. Reading a cheatsheet does not mean you have mastered Python. Mastery means choosing the right data structure, writing idiomatic code that fails loudly, testing the failure paths, and explaining why one approach is right when three look similar.
For a human developer, mastering Python means you can ship reliable programs: clean packaging with pyproject.toml, typed public APIs, correct concurrency (asyncio vs threads vs processes), validated config, structured logs, and tests that cover edge cases.
For an AI agent or language model, mastering Python means generating correct, maintainable code — not approximate snippets that mute exceptions, use mutable default arguments, or shell-interpolate user input. An agent that has mastered Python fetches full page markdown, follows an ordered curriculum, self-verifies with generation tasks, and refuses to claim competence from titles alone.
This page is the dual-audience curriculum for both. Humans follow the roadmap with runnable exercises. Agents ingest the same stages via the markdown API and Agent skills.
info
Use this outcomes checklist as the definition of done.
| Outcome | Human proof | AI agent proof |
|---|---|---|
| Idiomatic data structures | Picks list/dict/set/deque correctly | Avoids O(n) membership on lists |
| No mutable defaults | def f(x=None) pattern | Never emits list/dict defaults |
| Exceptions with context | raise X from e; no bare except | Preserves __cause__; typed errors |
| Pathlib over os.path | Path used for file work | Prefers pathlib.Path APIs |
| Concurrency matched to work | asyncio for I/O, ProcessPool for CPU | Explains GIL; picks right pool |
| Typed public surfaces | type hints + mypy/pyright clean | Annotates params/returns |
| Secrets never logged | logging filters / redact | Uses secrets, not random |
| Tests cover failure | pytest for happy + error paths | Writes fixtures and parametrize |
| Packaging modern | pyproject.toml + lockfile | uv/poetry, not setup.py alone |
| CLI & config validated | argparse/typer + pydantic | Validates env at startup |
Humans
- Follow Stages 0–6 in order with hands-on exercises
- Complete each checkpoint project before advancing
- Use pdb, pytest, ruff, and mypy as daily tools
- Rebuild examples from memory within 24 hours
AI agents
- Install forgelearn-python and fetch curriculum order
- Ingest full markdown per topic — not titles
- Generate artifacts and self-score verification prompts
- Fail closed on critical checklist misses
| 1 | curl -s https://forgelearn.dev/api/agent |
| 2 | curl -s https://forgelearn.dev/api/agent?skill=forgelearn-python |
| 3 | curl -s https://forgelearn.dev/api/agent?curriculum=python |
| 4 | curl -s "https://forgelearn.dev/api/markdown?path=python/mastery" |
| 5 | curl -s "https://forgelearn.dev/api/markdown?path=python/stdlib-reference" |
| 6 | curl -s https://forgelearn.dev/skills/forgelearn-python/SKILL.md -o SKILL.md |
| 7 | curl -s https://forgelearn.dev/llms-python.txt |
pro tip
danger
Seven ordered stages covering every Python topic on ForgeLearn. Complete topics and the mastery checkpoint before advancing.
Stage 0 — Foundations · ~8 hours
Syntax, types, control flow, strings, numbers.
| Topic | Path | Focus |
|---|---|---|
| 0 to Hero | /docs/python | setup, first programs |
| Variables & Types | /docs/python/basics | dynamic typing |
| Strings | /docs/python/strings | f-strings, slicing |
| Numbers & Math | /docs/python/numbers | int, float, decimal |
| Booleans | /docs/python/booleans | truthiness |
| Control Flow | /docs/python/loops | if/for/while |
| Match/Case | /docs/python/match-case | structural matching |
| Stdlib Reference | /docs/python/stdlib-reference | encyclopedia |
Stage 1 — Data Structures · ~10 hours
Lists, tuples, dicts, sets, collections, itertools.
| Topic | Path | Focus |
|---|---|---|
| Overview | /docs/python/data-structures | choosing structures |
| Lists | /docs/python/lists | slicing, methods |
| Tuples | /docs/python/tuples | immutability |
| Dictionaries | /docs/python/dicts | mapping patterns |
| Sets | /docs/python/sets | membership, algebra |
| Collections & Itertools | /docs/python/collections-itertools | Counter, deque, product |
| Comprehensions | /docs/python/comprehensions | list/dict/set comps |
| Generators | /docs/python/generators | yield, iterators |
Stage 2 — Functions & Modules · ~8 hours
Functions, lambdas, modules, functools, packaging surface.
| Topic | Path | Focus |
|---|---|---|
| Functions | /docs/python/functions | args, kwargs, scope |
| Lambdas | /docs/python/lambda | map, filter, sorted |
| Modules & Packages | /docs/python/modules | import system |
| Standard Library | /docs/python/stdlib | key modules tour |
| Functools | /docs/python/functools | cache, partial, wraps |
| Decorators | /docs/python/decorators | decorator patterns |
| Argparse | /docs/python/argparse | CLI design |
| Pathlib | /docs/python/pathlib | filesystem paths |
Stage 3 — OOP & Typing · ~12 hours
Classes, inheritance, dataclasses, descriptors, typing.
| Topic | Path | Focus |
|---|---|---|
| Classes & Instances | /docs/python/oop | __init__, self |
| Magic Methods | /docs/python/magic-methods | dunder protocol |
| Inheritance | /docs/python/inheritance | MRO, super() |
| Data Classes | /docs/python/dataclasses | @dataclass |
| Properties | /docs/python/properties | getters/setters |
| Descriptors | /docs/python/descriptors | __get__/__set__ |
| ABC & Enums | /docs/python/abc-enums | abstractmethod, Enum |
| Type Hints | /docs/python/type-hints | generics, Protocol |
| Protocols | /docs/python/protocols | structural typing |
| Slots | /docs/python/slots | memory layout |
| Metaclasses | /docs/python/metaclasses | type machinery |
| Pydantic | /docs/python/pydantic | runtime validation |
Stage 4 — I/O, Errors & Concurrency · ~12 hours
Files, JSON/CSV, logging, threading, asyncio, hashing.
| Topic | Path | Focus |
|---|---|---|
| Error Handling | /docs/python/errors | exceptions |
| File I/O | /docs/python/file-io | open, with |
| Context Managers | /docs/python/context-managers | contextlib |
| JSON & CSV | /docs/python/json-csv | serialization |
| Datetime & Time | /docs/python/datetime-time | zoneinfo |
| Hashlib & Secrets | /docs/python/hashlib-secrets | crypto primitives |
| Logging | /docs/python/logging | handlers, levels |
| Concurrency | /docs/python/concurrency | GIL overview |
| Threading | /docs/python/threading | Lock, Queue |
| Asyncio | /docs/python/asyncio | async/await |
| Async Patterns | /docs/python/async-patterns | TaskGroup |
| Multiprocessing | /docs/python/multiprocessing | Process, Pool |
Stage 5 — Testing & Tooling · ~8 hours
pytest, linting, environments, poetry/uv.
| Topic | Path | Focus |
|---|---|---|
| Testing | /docs/python/testing | pytest, fixtures |
| Packaging | /docs/python/packaging | wheels, PyPI |
| Poetry & uv | /docs/python/poetry-uv | lockfiles |
| Linting & Formatting | /docs/python/linting | ruff, mypy |
| Virtual Environments | /docs/python/environments | venv, conda |
| Best Practices | /docs/python/best-practices | PEP 8, structure |
| Walrus Operator | /docs/python/walrus | := |
| Regex | /docs/python/regex | re module |
Stage 6 — Ecosystem — Web & Data · ~12 hours
HTTP clients, FastAPI, Django, SQLAlchemy, data science.
| Topic | Path | Focus |
|---|---|---|
| Httpx & Requests | /docs/python/httpx-requests | HTTP clients |
| FastAPI | /docs/python/fastapi | async APIs |
| Django | /docs/python/django | ORM, admin |
| SQLAlchemy | /docs/python/sqlalchemy | 2.0 ORM/Core |
| Web Frameworks | /docs/python/web-frameworks | comparison |
| Data Science | /docs/python/data-science | NumPy, Pandas |
Each stage ends with a build-from-memory project. Do not skip.
Stage 0 checkpoint
CLI calculator with match/case for operators; f-string report; type-annotated functions; no bare except.
| 1 | def calc(a: float, op: str, b: float) -> float: |
| 2 | match op: |
| 3 | case "+": |
| 4 | return a + b |
| 5 | case "-": |
| 6 | return a - b |
| 7 | case "*" | "x": |
| 8 | return a * b |
| 9 | case "/" if b != 0: |
| 10 | return a / b |
| 11 | case _: |
| 12 | raise ValueError(f"unsupported op: {op!r}") |
Stage 1 checkpoint
Word-frequency Counter from a text file; deque sliding window; itertools.groupby on sorted keys; set algebra for unique tokens.
| 1 | from collections import Counter |
| 2 | from pathlib import Path |
| 3 | |
| 4 | text = Path("corpus.txt").read_text(encoding="utf-8") |
| 5 | counts = Counter(text.lower().split()) |
| 6 | print(counts.most_common(10)) |
Stage 2 checkpoint
Package with __init__.py exports; @lru_cache fib; argparse CLI that takes a Path; functools.wraps on a timing decorator.
| 1 | from functools import lru_cache, wraps |
| 2 | import time |
| 3 | |
| 4 | @lru_cache(maxsize=128) |
| 5 | def fib(n: int) -> int: |
| 6 | return n if n < 2 else fib(n - 1) + fib(n - 2) |
| 7 | |
| 8 | def timed(fn): |
| 9 | @wraps(fn) |
| 10 | def wrapper(*args, **kwargs): |
| 11 | t0 = time.perf_counter() |
| 12 | try: |
| 13 | return fn(*args, **kwargs) |
| 14 | finally: |
| 15 | print(f"{fn.__name__}: {time.perf_counter() - t0:.4f}s") |
| 16 | return wrapper |
Stage 3 checkpoint
Frozen dataclass + Pydantic model for the same DTO; custom descriptor validator; ABC with abstractmethod; StrEnum status codes.
| 1 | from dataclasses import dataclass |
| 2 | from enum import StrEnum |
| 3 | from abc import ABC, abstractmethod |
| 4 | |
| 5 | class Status(StrEnum): |
| 6 | OK = "ok" |
| 7 | ERR = "error" |
| 8 | |
| 9 | @dataclass(frozen=True, slots=True) |
| 10 | class User: |
| 11 | id: int |
| 12 | name: str |
| 13 | status: Status |
| 14 | |
| 15 | class Repository(ABC): |
| 16 | @abstractmethod |
| 17 | def get(self, id: int) -> User: ... |
Stage 4 checkpoint
Async httpx client with timeout; JSON/CSV round-trip; zoneinfo-aware datetime; hashlib file digest; TaskGroup fan-out.
| 1 | import asyncio |
| 2 | import hashlib |
| 3 | from pathlib import Path |
| 4 | |
| 5 | async def digest(path: Path) -> str: |
| 6 | data = await asyncio.to_thread(path.read_bytes) |
| 7 | return hashlib.sha256(data).hexdigest() |
| 8 | |
| 9 | async def main(paths: list[Path]) -> dict[str, str]: |
| 10 | async with asyncio.TaskGroup() as tg: |
| 11 | tasks = {p.name: tg.create_task(digest(p)) for p in paths} |
| 12 | return {k: t.result() for k, t in tasks.items()} |
Stage 5 checkpoint
pytest suite with fixtures + parametrize; ruff + mypy clean; poetry or uv lockfile; pyproject.toml with [project] metadata.
| 1 | # pyproject.toml excerpt |
| 2 | # [project] |
| 3 | # name = "mastery-s5" |
| 4 | # requires-python = ">=3.11" |
| 5 | # dependencies = ["httpx", "pydantic>=2"] |
| 6 | # |
| 7 | # [tool.pytest.ini_options] |
| 8 | # testpaths = ["tests"] |
| 9 | # |
| 10 | # [tool.ruff] |
| 11 | # line-length = 88 |
Stage 6 checkpoint
FastAPI CRUD with Pydantic models + Depends; SQLAlchemy 2.0 session; httpx TestClient tests; optional Django admin sketch.
| 1 | from fastapi import FastAPI, Depends |
| 2 | from pydantic import BaseModel, Field |
| 3 | |
| 4 | app = FastAPI() |
| 5 | |
| 6 | class ItemIn(BaseModel): |
| 7 | name: str = Field(min_length=1, max_length=100) |
| 8 | price: float = Field(gt=0) |
| 9 | |
| 10 | @app.post("/items") |
| 11 | async def create_item(item: ItemIn) -> ItemIn: |
| 12 | return item |
After each stage, agents (and humans) must generate code that passes these checks. Score critically: any critical miss = fail.
| Stage | Prompt | Critical fail if |
|---|---|---|
| 0 | Implement calc with match/case | Uses if-chain only; bare except |
| 1 | Count words with Counter + Path | Uses open() without encoding; list.count loop |
| 2 | Decorator with wraps + argparse Path | Missing @wraps; os.path only |
| 3 | ABC + frozen dataclass + StrEnum | Mutable default; no abstractmethod |
| 4 | Async digest with TaskGroup | Sync read in async; no timeout |
| 5 | pytest + pyproject + lock | No tests; setup.py only |
| 6 | FastAPI + Pydantic Field | No validation; sync blocking I/O |
| 1 | CRITICAL = [ |
| 2 | "mutable default argument", |
| 3 | "bare except:", |
| 4 | "except Exception: pass", |
| 5 | "os.path.join without pathlib for new code", |
| 6 | "random for secrets/tokens", |
| 7 | "requests without timeout", |
| 8 | "asyncio without cancellation/timeout awareness", |
| 9 | ] |
| 10 | |
| 11 | def score(generated: str) -> list[str]: |
| 12 | hits = [c for c in CRITICAL if c.split()[0] in generated or c in generated] |
| 13 | return hits # empty = pass |
Spaced practice
Read a topic once, implement the checkpoint from memory the next day, then again in three days. Passive scrolling does not stick.
Explain aloud
If you cannot explain why asyncio is wrong for CPU-bound NumPy, you have not mastered concurrency.
Diff against stdlib
Before reaching for a third-party package, check stdlib-reference. pathlib, argparse, dataclasses, and zoneinfo cover more than most people assume.
| 1 | # Morning: one topic markdown |
| 2 | curl -s "https://forgelearn.dev/api/markdown?path=python/pathlib" | less |
| 3 | # Afternoon: rewrite the examples without looking |
| 4 | # Evening: run ruff + mypy + pytest on your rewrite |
| Anti-pattern | Why it hurts | Fix |
|---|---|---|
| Mutable defaults | Shared state across calls | None + if x is None |
| Bare except | Swallows KeyboardInterrupt | except SpecificError |
| import * | Namespace pollution | Explicit imports |
| Global mutable config | Hidden coupling | pydantic-settings / DI |
| Sync I/O in async | Blocks event loop | asyncio.to_thread / httpx |
| str path soup | Bugs on Windows/rel paths | pathlib.Path |
| print debugging forever | No structure in prod | logging + levels |
| pip freeze > requirements | Unpinned transitive chaos | uv/poetry lock |
danger
| Need | Go to |
|---|---|
| Quick lookup — all key modules | Stdlib Reference |
| CLI design | argparse |
| Filesystem paths | pathlib |
| Runtime validation | Pydantic |
| Async APIs | FastAPI |
| ORM | SQLAlchemy |
| Package managers | Poetry & uv |
| Ordered human path | Python Roadmap |
| Agent wiring | Agent Connect |
Copy-paste session for agents installing the Python mastery skill:
| 1 | # 1. Connect permanently |
| 2 | curl -s https://forgelearn.dev/api/agent?skill=forgelearn-python -o /tmp/fl-python.json |
| 3 | |
| 4 | # 2. Curriculum order |
| 5 | curl -s https://forgelearn.dev/api/agent?curriculum=python |
| 6 | |
| 7 | # 3. Mastery + encyclopedia |
| 8 | curl -s "https://forgelearn.dev/api/markdown?path=python/mastery" -o mastery.md |
| 9 | curl -s "https://forgelearn.dev/api/markdown?path=python/stdlib-reference" -o stdlib.md |
| 10 | |
| 11 | # 4. Stage topic batch (example: Stage 2) |
| 12 | for p in functions lambda modules functools argparse pathlib; do |
| 13 | curl -s "https://forgelearn.dev/api/markdown?path=python/$p" -o "$p.md" |
| 14 | done |
| 15 | |
| 16 | # 5. Skill file |
| 17 | curl -s https://forgelearn.dev/skills/forgelearn-python/SKILL.md -o SKILL.md |
| 18 | |
| 19 | # 6. Full Python corpus index |
| 20 | curl -s https://forgelearn.dev/llms-python.txt |
note
You (or your agent) have mastered Python on ForgeLearn when all of the following are true:
- Stages 0–6 topics ingested or studied with checkpoints completed
- Verification prompts pass with zero critical failures
- Can explain GIL vs asyncio vs multiprocessing tradeoffs in under two minutes
- Can ship a FastAPI + Pydantic + SQLAlchemy service with tests and a lockfile
- Defaults to pathlib, secrets, zoneinfo, dataclasses/Pydantic, and typed APIs
| 1 | from dataclasses import dataclass |
| 2 | |
| 3 | @dataclass(frozen=True) |
| 4 | class MasteryGate: |
| 5 | stages_complete: int |
| 6 | critical_fails: int |
| 7 | can_explain_gil: bool |
| 8 | has_lockfile: bool |
| 9 | uses_pathlib_defaults: bool |
| 10 | |
| 11 | def passed(self) -> bool: |
| 12 | return ( |
| 13 | self.stages_complete >= 7 |
| 14 | and self.critical_fails == 0 |
| 15 | and self.can_explain_gil |
| 16 | and self.has_lockfile |
| 17 | and self.uses_pathlib_defaults |
| 18 | ) |
Community
Get help on Slack, Discord or VIP
Stuck on a guide? Join the community and ask.