🐍 The Complete Python Master Reference
CLI commands & subcommands · the whole language · standard library · full step-by-step tutorials · 10 library tutorials
The Python edition of the Command-Line Master Reference. Covers every way you run Python (interpreter flags,pip,venv, packaging tools, debuggers), the complete core language with every important method table, the essential standard library, 20 hands-on tutorials (T1–T10 core + L1–L10 libraries), and 8 quick-reference appendices. Examples target Python 3.10–3.13. Conventions:command= type exactly ·⟨required⟩·[optional]·a|b= choose one · ⚠ = destructive/risky · † = deprecated or removed (modern replacement given) ·$= shell prompt (CMD/PowerShell/Bash all fine) ·>>>= Python REPL.
Table of Contents
PART 0 — Getting Started (Beginner Course) 0.1 Python versions & implementations · 0.2 Installing Python everywhere · 0.3 Running Python: 7 ways · 0.4 The REPL & IPython · 0.5 Virtual environments & the packaging zoo · 0.6 Getting help anywhere · 0.7 Your first 10 programs
PART 1 — The Python Command Line: every tool & subcommand
python— every interpreter flag & env var · 2.pylauncher (Windows) · 3.pip— every subcommand & key flags · 4. Environments:venv·virtualenv·pipx·poetry·pipenv·conda·uv· 5.python -mtoolbox (pydoc, http.server, json.tool, timeit, cProfile, pdb, compileall, zipfile…)
- Packaging & publishing (
pyproject.toml,build,twine, setuptools) · 7. Testing (pytestall flags,unittestCLI,coverage) · 8. Quality tools (ruff,black,isort,mypy,flake8,pylint,bandit,pre-commit) · 9. Debugging & profiling (pdb— every command,cProfile,timeit,tracemalloc) · 10. Jupyter & notebooks
PART 2 — The Language: complete syntax reference
- Structure & syntax rules · 12. Built-in types — every method (
str,list,dict,set,bytes…) · 13. Operators & precedence — full tables · 14. Strings deep-dive: slicing, all methods, f-string format specs,reregex
- Control flow:
if/match/for/while, comprehensions, generators,itertools— every function · 16. Functions:*args/**kwargs, lambdas, closures, decorators,functools, typing · 17. Classes & OOP: inheritance,dataclasses, properties, dunder protocol · 18. Exceptions: full hierarchy + best practices · 19. Modules, packages & imports
- Files & I/O:
open,pathlib(all methods),os/shutil, JSON/CSV/pickle,sqlite3,tempfile· 21. Concurrency:threading,multiprocessing,asyncio,concurrent.futures· 22. Standard-library essentials:datetime,collections,itertools,random,hashlib,subprocess,argparse(full tutorial),logging(full tutorial), networking,zipfile
PART 3 — Hands-On Tutorials (T1–T10) + Library Tutorials (L1–L10) T1 Your first hour · T2 Parse a log file → CSV report · T3 Consume any REST API (JSON) · T4 Build a real CLI app with argparse · T5 OOP with dataclasses: an inventory manager · T6 Package & publish a library to PyPI · T7 Test like a pro with pytest · T8 Automate the boring stuff (files, Excel, email) · T9 Async: concurrent downloader · T10 Debug & profile like a detective L1 requests/httpx · L2 pydantic · L3 numpy · L4 pandas · L5 matplotlib · L6 typer+rich · L7 SQLAlchemy · L8 BeautifulSoup · L9 Pillow+openpyxl · L10 capstone — dotenv+tenacity+tqdm+rich together
PART 4 — Appendices A. A–Z command/tool index · B. All built-in functions (70) · C. Dunder methods protocol table · D. f-string & format() spec table · E. Exceptions hierarchy tree · F. One-liner cookbook · G. Traceback decoding, common errors, best-practice checklist · H. Every Python library — complete stdlib + essential third-party
PART 0 — GETTING STARTED (Beginner Course)
0.1 Python versions & implementations
| Thing | What to know |
|---|---|
| Python 3.x | The present & future. Use 3.11+ (25–60 % faster than 3.10); 3.13 is current stable |
| Python 2.7 † | Dead since 2020-01-01. If you meet legacy code, port it (2to3) |
| CPython | The reference implementation you download from python.org — what “Python” means by default |
| PyPy | JIT-compiled, often 4–10× faster for long-running pure-Python code; lags CPython by a version or two |
| MicroPython / CircuitPython | Python for microcontrollers (ESP32, Raspberry Pi Pico) |
| IronPython / Jython † | .NET / JVM ports — niche today |
Version check — do this first, always: $ python --version (or py -V on Windows). The interpreter reports e.g. Python 3.12.4. Any 3.10+ is fine for this entire document.
0.2 Installing Python everywhere
| Platform | Method |
|---|---|
| Windows | winget install Python.Python.3.13 (or from python.org — tick “Add python.exe to PATH” in the installer) |
| Windows (multi-version) | Install several side by side; the py launcher picks: py -3.12, py -0 lists all |
| macOS | brew install python@3.13 (macOS also ships a system python3 — check python3 -V) |
| Ubuntu/Debian | sudo apt install python3 python3-pip python3-venv python3-full |
| Fedora/RHEL | sudo dnf install python3 python3-pip |
| From source | ./configure && make -j && sudo make altinstall (use altinstall so you don’t shadow the system python) |
Rule #1: never install project packages into the system Python — use a virtual environment (0.5). On Windows, also grab VS Code + the Python extension; on any platform an editor with a language server (VS Code, PyCharm, Neovim+pyright) transforms the experience.
0.3 Running Python: 7 ways
| # | How | Example |
|---|---|---|
| 1 | REPL | $ python → >>> prompt (exit: exit() or Ctrl+D / Ctrl+Z⏎) |
| 2 | Script file | $ python hello.py |
| 3 | One-liner | $ python -c "print(2**38)" |
| 4 | Module | $ python -m http.server 8000 |
| 5 | Stdin pipe | $ echo "print('hi')" | python or python < script.py |
| 6 | Interactive-after-run | $ python -i script.py — drops into REPL with the script’s variables (great for poking at results) |
| 7 | Executable script | Add shebang #!/usr/bin/env python3, chmod +x, run ./script.py (Unix); on Windows use py script.py or .py file association |
Bonus: $ python -m pydoc -b opens browsable docs of every installed module; python -m this prints the Zen of Python.
0.4 The REPL & IPython
| REPL trick | What it does |
|---|---|
_ | Last result value |
_3 (IPython) | Result number 3 |
Tab | Completion (names, paths, methods) |
history (IPython) | Show session history |
help(x) | Docs on object x |
dir(x) | Everything x has |
x? / x?? (IPython) | Quick help / full source |
%timeit expr (IPython) | Micro-benchmark |
%run script.py (IPython) | Run file in session |
%pip install x (IPython/Jupyter) | Pip inside the right environment |
python -q | Start REPL without banner |
PYTHONSTARTUP=file env | Auto-run on every REPL start |
Upgrade once comfortable: $ pip install ipython → run ipython. Colored output, magic commands (%), ? help, shell escape (!ls), and syntax-highlighted input.
0.5 Virtual environments & the packaging zoo
The problem: project A needs requests==2.31, project B needs 2.32. Install both into one global Python → pain. The fix: per-project isolated environments.
python -m venv .venv # 1. create (one-time per project)
source .venv/bin/activate # 2. activate (Linux/macOS)
.venv\Scripts\activate # (Windows CMD/PowerShell)
python -m pip install -U pip # 3. fresh envs ship an old pip — upgrade
pip install requests # 4. install into the env only
deactivate # 5. leave the envAn env is just a folder (here .venv/) containing a copy/symlink of the interpreter plus its own site-packages. Delete the folder = delete the env. Commit a requirements.txt (pip freeze > requirements.txt), never the .venv/ folder itself.
| Tool | What it is | When to use |
|---|---|---|
venv | Built-in env creator | Default choice, zero install |
virtualenv | Third-party, faster, more features | Fine, but venv usually enough |
pip | Package installer from PyPI | Inside every env |
pipx | Installs CLI apps into their own envs (pipx install black) | For tools, not libraries |
poetry | Env + dependency + build + publish, all-in-one (pyproject.toml) | Modern library/app workflow |
pipenv | Pip + venv glue with Pipfile | Declined in popularity |
conda / mamba | Env manager for Python and C/Fortran stacks (science) | Data science, numpy/CUDA needs |
uv ⭐ | Rust-based, 10–100× faster pip/venv/poetry replacement | The 2024+ modern default for speed |
0.6 Getting help anywhere
| Where | Command |
|---|---|
| REPL | help(len), help("re"), dir(str) |
| Shell docs | python -m pydoc re.findall, pydoc -b (browser), pydoc -w module (writes .html) |
| Signatures live | import inspect; inspect.signature(func) |
| Official docs | docs.python.org/3 — tutorial, library reference, HOWTOs |
| Error → answer | Paste the last line of a traceback into a search; add “python3” |
| PEPs | Index of Python Enhancement Proposals — the design documents (peps.python.org) |
0.7 Your first 10 programs
# 1 — print & arithmetic
print("Hello, Python!", 2 + 2, 10 / 4, 10 // 4, 10 % 4, 2 ** 10)
# 2 — variables need no declarations
name, age = "Matin", 30
print(f"{name} will be {age + 1} next year") # f-string
# 3 — a list, a loop
for i in [3, 1, 4, 1, 5]:
print(i * "*")
# 4 — conditions
temperature = 31
print("hot" if temperature > 28 else "fine")
# 5 — function
def greet(who="world"):
return f"Hello, {who}!"
print(greet(), greet("Python"))
# 6 — dictionary
book = {"title": "Dune", "year": 1965}
book["author"] = "Frank Herbert"
print(book["title"].upper())
# 7 — input (note: always a string!)
age = int(input("Your age? "))
print("Days alive ≈", age * 365.25)
# 8 — file write & read
with open("notes.txt", "w", encoding="utf-8") as f:
f.write("line 1\nline 2\n")
print(open("notes.txt").read().splitlines())
# 9 — comprehension
squares = [n * n for n in range(1, 11) if n % 2 == 1]
print(squares)
# 10 — import & use the battery included
from datetime import date
from pathlib import Path
print(date.today(), "· cwd:", Path.cwd().name)PART 1 — THE PYTHON COMMAND LINE: EVERY TOOL & SUBCOMMAND
1. python — Every Interpreter Flag
| Flag | Meaning |
|---|---|
python script.py arg1 arg2 | Run script; args land in sys.argv[1:] |
python -c "code" | Run a one-liner (python -c "import sys;print(sys.version)") |
python -m module [args] | Run a library module as a program — the power user's secret (§5) |
python - | Read the program from stdin |
python -i [script] | Drop into the REPL after running (inspect live objects) |
python -q | No banner in REPL |
python -V / --version | Print version (add -VV for build+compiler detail) |
python -u | Unbuffered stdout/stderr — essential for logs in Docker/pipes |
python -B | Don't write .pyc bytecode caches |
python -B-adjacent: PYTHONPYCACHEPREFIX=dir | Redirect all bytecode caches to one folder |
python -E | Ignore all PYTHON* environment variables (clean runs) |
python -s | Ignore user site-packages |
python -S | Skip importing site at startup (minimal startup; you lose easy-install paths) |
python -I | Isolated mode = -E + -s + safe sys.path (no script dir) |
python -O / -OO | Optimizations: strip assert (-O), also docstrings (-OO) — sets __debug__=False |
python -P (3.11+) | Don't prepend script's directory to sys.path (safer imports) |
python -b / -bb | Warn / error on bytes↔str comparison traps |
python -v | Verbose: trace every import (also import verbose style debugging) |
python -W action | Warnings control: default error ignore always module once (-W error::DeprecationWarning) |
python -X dev | Development mode: extra checks, debug allocator (also -X utf8, -X frozen_modules=on) |
python -X importtime | Print per-module import timing — kills slow startup |
python --check-hash-based-pycs always | Always validate cached bytecode |
python -m pdb script.py | Start under the debugger (§9) |
python -m trace --trace script.py | Line-by-line execution trace |
Environment variables that matter
| Variable | Effect |
|---|---|
PYTHONPATH=dir1:dir2 | Extra module search paths (try to avoid; use proper packaging) |
PYTHONHOME | Relocate the stdlib — rarely needed, often harmful |
PYTHONSTARTUP=file | Executed at every interactive startup |
PYTHONBREAKPOINT=0|func | Disable or retarget breakpoint() (e.g. PYTHONBREAKPOINT=pudb.set_trace) |
PYTHONWARNINGS=ignore | Same as -W |
PYTHONDONTWRITEBYTECODE=1 | Same as -B |
PYTHONUNBUFFERED=1 | Same as -u (standard in Dockerfiles) |
PYTHONIOENCODING=utf-8 | Force stdio encoding (fix Windows accents) |
PYTHONHASHSEED=0 | Determinable hashing (reproducible dict order tests) |
PYTHONFAULTHANDLER=1 | Dump traceback on crashes (segfaults) |
VIRTUAL_ENV=path | Set by venv activation — how your prompt knows |
2. py Launcher (Windows)
| Command | Meaning |
|---|---|
py | Run newest installed Python 3 |
py -3.12 script.py | Run with a specific version |
py -0 / py --list | List installed versions (-0p with paths) |
py -V:3.12 | Pin exact version (also works in shebangs: #!/usr/bin/env python3.12 → py honors it) |
py -m venv .venv | Create env with chosen interpreter |
py -3.13 -m pip list | Pip of a specific version |
py -h | Launcher help |
On Unix the equivalent is simply python3.12 binaries on PATH, or uv python pin 3.12.
3. pip — Every Subcommand
Runpip --versioninside your venv to be sure you're installing into the right place. Universal flags on every subcommand:-v/-vv/-vvvverbosity,-qquiet,--dry-run(23.2+, install/list),--no-color,--require-virtualenvsafety.
| Subcommand | Purpose · key flags |
|---|---|
pip install pkg | Install latest from PyPI |
pip install pkg==1.4.2 | Exact pin · pkg>=1.4,<2 ranges · pkg!=1.5 exclusions |
pip install -r requirements.txt | Install a pinned set |
pip install -e . / -e path | Editable install (your local project, live-edited) |
pip install -U pkg | Upgrade (--upgrade) |
pip install --pre pkg | Allow pre-releases/betas |
pip install pkg -t dir | Install into arbitrary target folder |
pip install -i URL pkg | Alternate index (--index-url), --extra-index-url URL secondary |
pip install -f URL/pkg.whl | Find links — direct wheel/house index |
pip install --no-deps pkg | Skip dependency resolution |
pip install --only-binary=:all: | Wheels only (no source builds) · --no-binary pkg |
pip install --require-hashes -r reqs | Hash-pinned reproducible install |
pip install --report report.json pkg | JSON manifest of what would be installed |
pip download pkg -d dir | Fetch packages without installing |
pip uninstall pkg | Remove (-y no-confirm; multiple names ok) |
pip list | Installed packages — --outdated, --uptodate, -e (editables), --format=freeze|json|columns |
pip freeze | Requirements-style output — > requirements.txt |
pip show pkg | Version, location, deps, home page (-f = installed files) |
pip check | Verify installed deps are mutually consistent (missing/conflicting) |
pip cache dir|info|list|remove|purge | Manage the wheel cache |
pip config list|get|set|edit|unset | Config file management (--user, --global, --site scopes) |
pip index versions pkg | All versions available on the index |
pip hash file | Compute hash for --require-hashes |
pip inspect | Full environment report as JSON (3.11+ pip) |
pip wheel pkg | Build wheels for packages |
pip debug | Environment/pip diagnostics |
pip completion --bash|zsh|fish | Shell autocompletion setup |
pip search † | Removed (PyPI shut the endpoint) — use pypi.org search or pip index |
pip help [command] | Built-in docs |
The workflow that never breaks: python -m pip … — guarantees pip belongs to that interpreter, however many Pythons you have.
4. Environment & Dependency Managers
venv (built-in)
| Command | Meaning |
|---|---|
python -m venv .venv | Create |
python -m venv .venv --system-site-packages | Inherit global packages (rare) |
python -m venv .venv --upgrade-deps | Create with fresh pip+setuptools |
python -m venv .venv --without-pip | Bare env (then ensurepip) |
python -m ensurepip --upgrade | Bootstrap pip into an env |
pipx — CLI apps in isolated envs
| Command | Meaning |
|---|---|
pipx install black | Install an app globally-but-isolated, ~/.local/bin/black |
pipx install poetry --pip-args=... | Install with pip args |
pipx list | Installed apps |
pipx upgrade / upgrade-all | Update |
pipx uninstall / uninstall-all | Remove |
pipx inject app pkg | Add a library into an app's env |
pipx run pkg@1.2 --args | One-shot run without installing |
pipx ensurepath | Fix PATH |
poetry
| Command | Meaning |
|---|---|
poetry init | Interactive pyproject.toml |
poetry new pkg / poetry install | Scaffold project / install all deps |
poetry add requests / poetry add --group dev pytest | Add dependency (respecting semver) |
poetry remove pkg | Drop it |
poetry update / poetry lock | Refresh deps / regenerate lock file |
poetry run pytest | Run command inside env |
poetry shell | Activate env shell |
poetry build | sdist + wheel |
poetry publish | To PyPI (--build both) |
poetry env info / use 3.12 | Env management |
conda / mamba
| Command | Meaning |
|---|---|
conda create -n ml python=3.12 numpy | New env with packages |
conda activate ml / conda deactivate | Enter/leave |
conda install pkg / conda remove pkg | Install/remove (conda-forge channel: -c conda-forge) |
conda env list / conda env export > env.yml | List / export |
conda env create -f env.yml | Recreate |
conda list / conda info / conda clean --all | Inventory / info / cache purge |
conda update conda | Self-update |
uv — the fast modern toolchain
| Command | Meaning |
|---|---|
uv venv / uv venv -p 3.13 | Create env (blazing) |
uv pip install / list / freeze / uninstall … | pip-compatible interface |
uv init project / uv add pkg / uv remove pkg | Project management (pyproject+lock) |
uv sync | Install exactly the lock file |
uv lock / uv upgrade | Lock / upgrade deps |
uv run script.py | Run inside the project env |
uv tool install ruff / uvx ruff check . | pipx-style tools / ephemeral run |
uv python install 3.12 / uv python list | Manage interpreters themselves |
uv self update | Update uv |
5. The python -m Toolbox
python -m module runs any module that has a __main__. The stdlib ships dozens of ready-made tools:
| Command | What it gives you |
|---|---|
python -m http.server 8000 | Instant web server for the current folder (add --bind 127.0.0.1, --directory path) |
python -m http.server 8000 --cgi † | CGI mode (removed 3.13) |
python -m json.tool file.json | Pretty-print / validate JSON (--indent 2, - stdin) |
python -m json.tool --json-lines f.ndjson | Validate newline-delimited JSON |
python -m timeit "‘-’.join(map(str,range(50)))" | Micro-benchmark a snippet |
python -m cProfile -s cumulative script.py | Profile a whole program (§9) |
python -m pstats dump.prof | Interactive profile browser |
python -m pdb script.py | Run under the debugger |
python -m pydoc re.sub / pydoc -b / -p 1234 | Docs in terminal / browser / port |
python -m zipfile -l arch.zip | List zip contents |
python -m zipfile -e arch.zip dir/ / -c arch.zip files… | Extract / create |
python -m tarfile -l/-e/-c … | Same for tar archives |
python -m base64 file | Encode/decode base64 from CLI |
python -m compileall src/ | Pre-compile bytecode (deployment/syntax check) |
python -m ensurepip --upgrade | Bootstrap/repair pip |
python -m site | Show module search paths |
python -m sysconfig | Build/interpreter configuration |
python -m unittest discover | Run test suite (§7) |
python -m venv .venv | Environments (§4) |
python -m webbrowser -t "https://x" | Open a URL in a browser |
python -m ast script.py | Dump the parse tree |
python -m dis script.py | Disassemble to bytecode |
python -m token | Tokenizer dump |
python -m textwrap? — no CLI | ✗ (library only; listed so you don't hunt) |
python -m this | The Zen of Python 🎶 |
python -m antigravity | 🪁 xkcd 352. You deserve a break |
6. Packaging & Publishing
Anatomy of a pyproject.toml (the modern standard — one file, no setup.py needed):
[build-system]
requires = ["setuptools>=69"]
build-backend = "setuptools.build_meta"
[project]
name = "coolpkg" # PyPI name
version = "1.0.0" # or dynamic from git tags
description = "Does cool things"
readme = "README.md"
requires-python = ">=3.10"
license = {text = "MIT"}
authors = [{name = "Matin", email = "you@example.com"}]
keywords = ["cli", "tools"]
classifiers = ["Programming Language :: Python :: 3", "License :: OSI Approved :: MIT License"]
dependencies = ["requests>=2.31", "rich>=13"]
[project.optional-dependencies]
dev = ["pytest", "ruff", "mypy"]
[project.scripts]
cool = "coolpkg.cli:main" # creates the `cool` command on install
[project.urls]
Homepage = "https://github.com/xvviix/coolpkg"
[tool.setuptools.packages.find]
include = ["coolpkg*"]| Tool | Commands |
|---|---|
pip install build → python -m build | Produces dist/*.whl + dist/*.tar.gz |
pip install twine → twine check dist/* | Validate metadata/readme rendering |
twine upload dist/* | Publish to PyPI (token auth: __token__ + API token) |
twine upload --repository testpypi dist/* | Rehearse on TestPyPI first |
pip install dist/coolpkg-1.0.0-py3-none-any.whl | Local install test |
Legacy setup.py † | python setup.py sdist bdist_wheel — migrate to pyproject |
Version bumping: SemVer — MAJOR breaking / MINOR features / PATCH fixes. Dev releases: 1.1.0a1, 1.1.0b2, 1.1.0rc1.
7. Testing
pytest — the standard
| Invocation | Meaning |
|---|---|
pytest | Run all tests in test_*.py / *_test.py |
pytest test_math.py / pytest test_math.py::test_add | File / single test |
pytest -v / -vv / -q | Verbose / extra-verbose / quiet |
pytest -x | Stop on first failure (--maxfail=3) |
pytest -k "add and not slow" | Select by keyword expression |
pytest -m slow | Select by marker (@pytest.mark.slow) |
pytest -s | Disable capture — see print() output |
pytest --lf / --ff | Last-failed only / failed-first |
pytest --durations=10 | Slowest 10 tests |
pytest --tb=short|long|line|no | Traceback style |
pytest --cov=mypkg --cov-report=html | Coverage (needs pytest-cov) → htmlcov/ |
pytest -n auto | Parallel (pytest-xdist) |
pytest --fixtures / --collect-only | List fixtures / dry-run collection |
pytest --junitxml=report.xml | CI output |
pip install pytest-mock → mocker.patch("mod.func") | Mocking helper |
Core API (used in scripts): assert, pytest.raises, pytest.fixture, pytest.mark.parametrize, pytest.approx, tmp_path, capsys, monkeypatch.
unittest CLI (stdlib)
| Command | Meaning |
|---|---|
python -m unittest | Autodiscover (discover -s tests -p "test_*.py") |
python -m unittest tests.test_math | One module |
python -m unittest tests.test_math.TestAdd.test_ints | One test |
-v / -q | Verbosity |
-f / -c / -b | Stop-first-fail / fast Ctrl+C / buffer output |
-k pattern | Filter by name |
--locals | Show locals in tracebacks |
coverage
pip install coverage → coverage run -m pytest → coverage report -m (missing lines) → coverage html. Config in .coveragerc or pyproject [tool.coverage.run].
8. Quality Tools — Lint, Format, Type-check
| Tool (install) | Command | Purpose |
|---|---|---|
ruff ⭐ | ruff check . | Linter (100s of rules, flake8+isort+pyupgrade in one, Rust-fast) |
ruff check --fix . | Auto-repair | |
ruff format . | Formatter (Black-compatible) | |
ruff rule RUF008 / ruff linter | Rule docs / list rules | |
black | black . | The formatter — --check, --diff, --line-length 100 |
isort | isort . | Sort imports (--profile black) |
mypy | mypy src/ | Static types — --strict, --ignore-missing-imports |
flake8 | flake8 src/ | Classic linter (--max-line-length 100) |
pylint | pylint src/ | Deep style/score linter |
bandit | bandit -r src/ | Security scanner |
pre-commit | pre-commit run -a | Run all hooks; .pre-commit-config.yaml glues the above into git |
One modern combo to rule them all: uv tool install ruff + ruff check --fix . && ruff format . + mypy src in CI. That replaces flake8+isort+black+pyupgrade at ludicrous speed.
9. Debugging & Profiling
pdb — every command
Start: python -m pdb script.py · or drop in code: import pdb; pdb.set_trace() · or modern: breakpoint().
| Pdb command | Action |
|---|---|
h / h cmd | Help / help on command |
l [n] / ll | List source (around line) / whole function |
w / where | Stack trace, current frame marked |
u / d | Up / down the stack frames |
b 42 / b func / b file:line | Set breakpoint |
b | List breakpoints |
tbreak … | Temporary breakpoint (dies after first hit) |
cl [n] | Clear breakpoint(s) |
disable n / enable n | Toggle without deleting |
condition n expr | Conditional breakpoint |
ignore n count | Skip next count hits |
commands n | Auto-commands on hit (end with end; silent + print = logging) |
s / step | Step into |
n / next | Step over |
r / return | Run until current function returns |
c / continue | Run to next breakpoint |
unt [n] | Run until line n (or next line > current) |
j n / jump | Jump execution to line n (careful!) |
a / args | Current function arguments |
p expr / pp expr | Print / pretty-print |
whatis expr | Type of expr |
source expr / display [expr] | Show source of object / auto-print on each stop |
interact | Full REPL at this point |
retval / exc | Last return value / current exception |
alias name cmd | Command macros |
exec stmt | Execute statement in frame |
q | Quit (hard exit) |
Better-looking alternatives: pip install ipdb (import ipdb; ipdb.set_trace()) or pudb (full-screen TUI), or just use your editor's debugger (VS Code: F5).
cProfile
python -m cProfile -s cumulative script.py # sorted by total time
python -m cProfile -o out.prof script.py # save
python -m pstats out.prof # browse: sort cumulative / stats 10 / stripReading it: ncalls calls · tottime time in the function itself · cumtime including children · look for high tottime = your hotspot. GUI: pip install snakeviz → snakeviz out.prof.
timeit / tracemalloc
python -m timeit "'-'.join(map(str, range(50)))"
python -m timeit -s "x=list(range(1000))" "x.sort()"| Snippet | Use |
|---|---|
timeit.timeit(f, number=1_000) | Library form |
python -X importtime -c "import requests" 2> imp.log | Find slow imports |
tracemalloc.start() … tracemalloc.take_snapshot() | Who allocates memory |
sys.settrace / sys.setprofile | Build your own tracer |
faulthandler.enable() (or -X dev) | Traceback on hard crashes |
10. Jupyter & Notebooks
| Command | Meaning |
|---|---|
pip install notebook jupyterlab | Install |
jupyter lab / jupyter notebook | Launch UI (browser tab) |
jupyter lab --no-browser --port=8889 | Headless/remote |
jupyter console / jupyter qtconsole | Terminal / Qt console |
jupyter kernelspec list | Installed kernels |
ipykernel → python -m ipykernel install --user --name venv --display-name "Py(venv)" | Register a venv as kernel |
jupyter nbconvert --to script nb.ipynb | Notebook → .py (--to html/slides/pdf) |
jupyter nbconvert --execute nb.ipynb --inplace | Run all cells from CLI |
jupyter trust nb.ipynb | Trust stored outputs |
jupyter --version / jupyter --data-dir | Install info |
PART 2 — THE LANGUAGE: COMPLETE SYNTAX REFERENCE
11. Structure & Syntax Rules
| Rule | Detail |
|---|---|
| Indentation | Blocks are defined by indentation (4 spaces by convention; never mix tabs/spaces) |
| Comments | # to end of line; docstrings: """…""" first statement of module/class/function |
| Line continuation | \ at EOL, or implicit inside ( ), [ ], { } |
| Statements per line | a = 1; b = 2 (semicolon) — discouraged |
| Names | Letters/digits/underscore, can't start with digit; lower_snake for functions/vars, CapWords for classes, _private convention, __name__ dunders are reserved |
| Case | Everything is case-sensitive |
| Entry point | if __name__ == "__main__": main() — runs only when executed, not imported |
| Encoding | UTF-8 by default (declare only if you need something else) |
| Truthiness | False: None, False, 0, 0.0, '', [], {}, set(), (), range(0) — everything else truthy |
| Identity vs equality | is compares identity (same object), == compares value; use is None, never == None |
12. Built-in Types — Every Method
Numeric
| Type | Notes / operations |
|---|---|
int | Arbitrary precision! 7 // 2=3 floor, 7 % 2=1, 2 ** 64, int("ff", 16)=255, int("0b101", 0), abs(), divmod(7,2)→(3,1), pow(b, e, mod) fast modular |
float | IEEE 754: 0.1 + 0.2 != 0.3 (use math.isclose, decimal, or fractions); 1e9, round(x, 2) banker's rounding, float("nan"), math.isnan |
complex | 3+4j, .real, .imag, abs() = magnitude |
bool | Subclass of int: True + True = 2 |
decimal.Decimal | Exact decimal money math: Decimal("0.10") + Decimal("0.20") == Decimal("0.30") |
fractions.Fraction | Exact rationals: Fraction(1, 3) * 3 == 1 |
str — all methods
| Method | Returns |
|---|---|
s.upper() lower() capitalize() title() swapcase() casefold() | Case variants (casefold = aggressive, for matching) |
s.strip() lstrip() rstrip([chars]) | Trim edges (default whitespace) |
s.ljust(w) rjust(w) center(w[, fill]) | Pad to width |
s.zfill(w) | Zero-pad (keeps sign) |
s.startswith(p) endswith(p) | Prefix/suffix test (accept tuples) |
s.find(sub) rfind(sub) | Index or −1 |
s.index(sub) rindex(sub) | Index or ValueError |
s.count(sub) | Occurrences |
s.replace(old, new[, count]) | Substitute |
s.split(sep) rsplit(sep) splitlines() | Split (None = whitespace runs; splitlines respects \n \r\n) |
s.join(iterable) | Glue: ",".join(words) |
s.partition(sep) rpartition(sep) | (pre, sep, post) 3-tuple |
s.removeprefix(p) removesuffix(p) | Clean prefix/suffix removal (3.9+) |
s.strip-family + chars arg | Trim specific chars |
s.encode("utf-8") | → bytes (errors="ignore|replace|strict") |
s.format(**kw) / s.format_map(m) | "{x:>10}".format(x=1) |
s.maketrans(a,b) + s.translate(t) | Character mapping |
Tests: isalnum() isalpha() isascii() isdecimal() isdigit() isnumeric() isidentifier() islower() isupper() isspace() istitle() isprintable() | True/False |
list & tuple
| Method | Returns |
|---|---|
l.append(x) | Add at end |
l.extend(iter) | Add many |
l.insert(i, x) | Insert at position |
l.remove(x) | Delete first ==x (ValueError if missing) |
l.pop([i]) | Remove & return (default last) |
l.clear() | Empty it |
l.index(x[, start[, end]]) | Position or ValueError |
l.count(x) | Occurrences |
l.sort(*, key=None, reverse=True) | In-place sort |
l.reverse() | In-place reverse |
l.copy() | Shallow copy |
sorted(iter, key=, reverse=) → new list · reversed(iter) → iterator | Non-mutating versions |
tuple | Immutable — only count + index; (1,) single needs comma |
min/max/sum/any/all(iter) | Aggregates |
dict
| Method | Returns |
|---|---|
d[k] | Get or KeyError |
d.get(k[, default]) | Get or default (None) |
d[k] = v / del d[k] / d.pop(k[, default]) / d.popitem() | Write / delete (KeyError) / delete+return / delete last inserted |
d.update(other) | Merge in |
d.setdefault(k, default) | Get-or-create |
d.keys() values() items() | Views (live!) |
dict(**kw), dict(zip(ks, vs)), d | other (3.9+), d |= other | Construction / merge |
{k: v for k, v in pairs} | Dict comprehension |
collections.OrderedDict† / defaultdict / Counter | When plain dict isn't enough (§22) |
set / frozenset
| Method | Returns |
|---|---|
s.add(x) / s.remove(x) / s.discard(x) | Add / remove (KeyError) / remove (silent) |
s.pop() / s.clear() | Arbitrary removal / empty |
s.update(*others) | |= union in place |
s.union(o) s.intersection(o) s.difference(o) s.symmetric_difference(o) | New set results — operators: | & - ^ |
s.issubset(o) s.issuperset(o) s.isdisjoint(o) | Relations: <= >= |
{x for x in it if p} | Set comprehension |
frozenset(iter) | Immutable, hashable (usable as dict key) |
bytes / bytearray / memoryview
| Method | Returns |
|---|---|
b"abc" / bytes(range(65,68)) / "a".encode() | Literal / from ints / from str |
b.decode("utf-8") | → str |
b.hex() / bytes.fromhex("414243") | Hex shuttle |
bytearray | Mutable bytes — same methods as bytes + append/extend/insert |
memoryview(b) | Zero-copy slice view (mv[0], mv.cast("I")) |
Other core types
| Type | Notes |
|---|---|
None | The null singleton — test with is None |
range(start, stop, step) | Lazy integer sequence — range(10, 0, -2) |
enumerate(it, start=0) | (index, item) pairs |
zip(a, b, strict=False) | Parallel iteration; strict=True errors on length mismatch (3.10+) |
slice(start, stop, step) | s[slice(1,None,2)] == s[1::2] |
type(x) / isinstance(x, T) / issubclass(A, B) | Type tools |
13. Operators & Precedence
| Category | Operators (high→low precedence order) |
|---|---|
| Grouping | ( ) |
| Call / index / attribute / slice | f(x) x[i] x.attr x[a:b] |
| Power | ** |
| Unary | +x -x ~x |
| Multiply/divide | * / // % |
| Add/subtract | + - |
| Shifts | << >> |
| Bitwise AND | & |
| Bitwise XOR | ^ |
| Bitwise OR | | |
| Comparisons / membership / identity | < <= > >= == != in not in is is not (chaining works: 1 < x < 10) |
| not | not x |
| and | a and b |
| or | a or b |
| Conditional | x if c else y |
| Assignment | = += -= *= /= //= %= **= &= |= ^= <<= >>= :=(walrus) |
Walrus := assigns inside an expression: while (chunk := f.read(8192)): …. Floor // rounds toward −∞ on negatives: -7 // 2 == -4. == on is-looking things: small ints/strings may be interned — never rely on it.
14. Strings Deep-Dive
Slicing — the complete rules
s = "Pythonista"
s[0] s[-1] # 'P', 'a'
s[2:5] s[:4] s[6:] # 'tho', 'Pyth', 'ista'
s[::2] s[::-1] # 'PtoiA' step-2; reversed copy
s[1:-1:3] # start 1, stop before -1, step 3Index out of range in [i] raises; slices never do (they clamp). Works identically on lists/tuples — learn once, use everywhere.
f-strings & format — full spec mini-language
f"{value:[[fill]align][sign][z][#][0][width][grouping][.precision][type]}"
| Piece | Options |
|---|---|
| align | < left · > right · ^ center · = pad after sign |
| sign | + always · - only negatives · space for positives |
# | Alternate form: 0x prefix, keep trailing .0 |
0 | Zero-pad (same as fill=0, align==) |
| width | Minimum width (can nest: {v:{w}}) |
| grouping | _ or , thousands separators |
| precision | Digits after . |
| type | d int · f fixed · e sci · g general · % percent · b o x X int bases · c char · s string · n locale-aware |
f"{3.14159:.2f}" # '3.14'
f"{1234567:,}" # '1,234,567'
f"{0.85:.1%}" # '85.0%'
f"{255:#x} {255:b}" # '0xff' '11111111'
f"{'hi':^10}" # ' hi '
f"{name=} / {x=}" # 'name=...' debug prints the expression!
f"{dt:%Y-%m-%d}" # datetime inline
f"{obj!r}" # repr() instead of str()re — the regex module
| Function | Use |
|---|---|
re.search(p, s) | First match anywhere → Match or None |
re.match(p, s) | Anchored at start · re.fullmatch entire string |
re.findall(p, s) | All matches (list; groups→tuples) |
re.finditer(p, s) | Iterator of Match objects |
re.sub(p, repl, s, count=0) | Replace (repl may be a function!) · re.subn also returns count |
re.split(p, s) | Split by pattern |
re.compile(p) | Precompiled pattern object — reuse it |
re.escape(s) | Quote all metacharacters |
Match m | m.group(0/1/name), m.groups(), m.groupdict(), m.start() end() span(), m[0] |
Flags: re.I ignorecase · re.M ^$ per-line · re.S dot-matches-newline · re.X verbose · re.A ASCII. Pattern syntax: \d \w \s \b classes, + * ? {m,n} reps, [] sets, (...) group, (?:...) non-capture, (?P<name>…), lookarounds (?=…) (?!…) (?<=…) (?<!…), alternation |.
15. Control Flow
Conditionals
if x > 100: ...
elif x > 10: ...
else: ...
# ternary
label = "big" if x > 100 else "small"
# match (3.10+) — structural pattern matching
match command.split():
case ["go", direction]: print("moving", direction)
case ["go", *rest]: print("go many:", rest)
case ["drop", item] if item != "": print("dropping", item)
case ["quit" | "exit"]: break
case {"op": "add", "n": n}: total += n # mapping patterns
case Point(x=0, y=y): ... # class patterns
case _: print("unknown")Loops
for item in iterable: ... # the one loop
for i, item in enumerate(items, 1): ...
for a, b in zip(xs, ys): ...
while condition: ...
break # exit loop
continue # next iteration
else: # runs only if loop finished WITHOUT break — unique to Python
print("no break happened")
for n in range(10, 0, -1): ... # countdownComprehensions — all four
[n*n for n in range(10) if n % 2 == 0] # list
{n: n*n for n in range(5)} # dict
{ch for ch in "abracadabra" if ch in "abc"} # set
(lazy := (line.strip() for line in f)) # generator expression — memory-free
[[r*c for c in range(4)] for r in range(3)] # nested
[x if x > 0 else 0 for x in data] # conditional expression inside
flat = [x for row in matrix for x in row] # flatten (order: outer loop first)Generators & iterators
def countdown(n):
while n > 0:
yield n # pause here, hand value out
n -= 1
gen = (x*x for x in range(1_000_000)) # lazy, O(1) memory
next(gen) # pull one value
def fib(): # infinite stream
a, b = 0, 1
while True:
yield a; a, b = b, a + b
# yield from delegates to a sub-generator
def chain_all(*iterables):
for it in iterables:
yield from it
# send / close — coroutine-style generators
g = echo(); g.send(None); g.send("hi")Protocol: iter(obj) → iterator; next(it) → value or StopIteration. Anything with __iter__ is iterable; make your class iterable by defining __iter__ (generator method is easiest).
itertools — every function
| Function | Yields |
|---|---|
count(10, 2) | 10, 12, 14, … forever |
cycle("AB") | A, B, A, B… |
repeat(x, n) | x, x, x (n times or forever) |
chain(a, b) / chain.from_iterable(lists) | Concatenation |
compress(data, [1,0,1]) | Keep items where selector truthy |
dropwhile(pred, it) / takewhile(pred, it) | Skip/keep until pred fails |
filterfalse(pred, it) | Opposite of builtin filter |
groupby(it, key) | Consecutive groups — sort first! |
islice(it, 5) / islice(it, 2, 10, 2) | Slicing any iterator |
pairwise(it) (3.10+) | (a,b),(b,c),… sliding pairs |
starmap(f, [(1,2),(3,4)]) | f(*args) per item |
tee(it, 2) | Two independent iterators |
zip_longest(a, b, fillvalue=0) | Zip to the longest |
product("AB", "12") | Cartesian product |
permutations("ABC", 2) | Ordered arrangements |
combinations("ABC", 2) | Unordered subsets |
combinations_with_replacement("AB", 2) | With repeats |
accumulate([1,2,3]) / accumulate(xs, operator.mul) | Running totals/products |
16. Functions
Definitions — the full anatomy
def f(pos_only, /, normal, *args, kw_only=1, **kwargs):
"""Docstring. pos_only is positional-only (the /)."""
...
f(1, 2, 3, 4, kw_only=9, anything="goes")
# pos_only=1, normal=2, args=(3,4), kwargs={'anything':'goes'}
def g(a, b=(), /, *, key=None): ... # / left = positional-only, * right = keyword-only
lambda x, y: x + y # expression function
func(*args_list, **kwargs_dict) # unpacking at call siteDefaults are evaluated once — the mutable default trap: def f(x, acc=[]) accumulates forever; use acc=None + acc = [] if acc is None else acc.
Closures, decorators, functools
def make_multiplier(n):
def multiply(x): return x * n # closes over n
return multiply
import functools
@functools.cache # memoize (3.9+); @lru_cache(maxsize=None) same
def fib(n): return n if n < 2 else fib(n-1) + fib(n-2)
@functools.wraps(f) # preserve metadata in custom decorators
def wrapper(*a, **k): ... functools tool | Does |
|---|---|
cache / lru_cache(maxsize=128, typed=False) | Memoization |
cached_property | Compute once, store on instance |
partial(f, x=1) | Freeze arguments |
reduce(f, iter, init) | Rolling fold: reduce(operator.add, xs, 0) |
wraps | Decorator-preserving decorator |
total_ordering | Define __eq__+__lt__, get all six comparisons |
cmp_to_key(f) | old-style cmp → sort key |
singledispatch | Function overloading by first-arg type |
Typing (annotations)
def process(items: list[int], name: str = "x", *,
limit: int | None = None) -> dict[str, float]: ...
from typing import Optional, Union, Literal, TypedDict, Protocol, Callable, TypeVar, Generic, Any
Vector = list[float] # type alias (or `type Vector = list[float]` 3.12+)
T = TypeVar("T")
class Repo(Generic[T]):
def get(self, id: int) -> T | None: ...
def scale(v: list[float], k: float = 1.0) -> list[float]: return [x * k for x in v]
# gradual typing: hints are NOT enforced at runtime — check with mypy / pyrightCommon hints: int str bytes bool float · list[T] dict[K,V] set[T] tuple[int, ...] · T \| None (3.10+) · Literal["a","b"] · Callable[[int], str] · Iterable/Sequence/Mapping (accept broad, return precise) · Any, Never, Self (3.11+).
17. Classes & OOP
class Animal:
kingdom = "Animalia" # class attribute (shared)
def __init__(self, name: str, legs: int = 4):
self.name = name # instance attributes
self.legs = legs
def speak(self) -> str:
return "..."
def __repr__(self):
return f"Animal({self.name!r})" # unambiguous (debug) repr
def __str__(self):
return f"{self.name} ({self.legs} legs)" # friendly str
class Dog(Animal): # inheritance
def __init__(self, name):
super().__init__(name, 4) # call parent init
def speak(self): # override
return "Woof"
d = Dog("Rex")
isinstance(d, Animal) → True; issubclass(Dog, Animal) → True| Concept | Syntax | Notes |
|---|---|---|
| Class attr vs instance | Dog.kingdom vs d.name | Shared vs per-object |
| Property | @property + @x.setter + @x.deleter | Computed attributes with validation |
| Classmethod | @classmethod def from_str(cls, s): | Alternative constructors |
| Staticmethod | @staticmethod def helper(): | No self/cls — namespaced function |
__slots__ = ("a","b") | Attribute whitelist | Smaller objects, no dict, blocks typos |
@dataclass | See below | Kills boilerplate |
| Multiple inheritance | class A(B, C) | MRO via A.__mro__ / super() follows it |
| Mixin | Small base adding one behavior | Convention: suffix Mixin |
| Abstract base | from abc import ABC, abstractmethod | Force subclasses to implement |
| Protocol | class Closeable(Protocol): def close(self)->None: ... | Duck-typed interfaces (static check) |
| Composition > inheritance | self.engine = Engine() | Prefer it when tempted by deep trees |
dataclasses — the boilerplate killer
from dataclasses import dataclass, field
@dataclass
class Point:
x: float
y: float = 0.0
tags: list[str] = field(default_factory=list) # mutable-safe default
def dist(self): return (self.x**2 + self.y**2) ** 0.5
p = Point(3, 4) # __init__ generated
p == Point(3, 4) # True — __eq__ generated
print(p) # Point(x=3, y=4) — __repr__ generatedModifiers: @dataclass(frozen=True) immutable/hashable · @dataclass(order=True) adds < etc. · field(compare=False, repr=False). Sister tools: typing.NamedTuple (tuple-behavior) and attrs (third-party, more features).
18. Exceptions
Full builtin hierarchy
BaseException
├── SystemExit # sys.exit()
├── KeyboardInterrupt # Ctrl+C
├── GeneratorExit
└── Exception
├── ArithmeticError
│ ├── ZeroDivisionError
│ ├── OverflowError
│ └── FloatingPointError
├── AssertionError
├── AttributeError
├── BufferError
├── EOFError
├── ImportError
│ └── ModuleNotFoundError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── MemoryError
├── NameError
│ └── UnboundLocalError
├── OSError
│ ├── FileNotFoundError
│ ├── FileExistsError
│ ├── PermissionError
│ ├── IsADirectoryError / NotADirectoryError
│ ├── BlockingIOError / InterruptedError
│ ├── TimeoutError
│ ├── ConnectionError (Reset/Aborted/Refused)
│ └── ProcessLookupError / ChildProcessError
├── ReferenceError
├── RuntimeError
│ ├── NotImplementedError
│ └── RecursionError
├── StopIteration / StopAsyncIteration
├── SyntaxError
│ └── IndentationError
│ └── TabError
├── SystemError
├── TypeError
├── ValueError
│ └── UnicodeError (Decode/Encode/Translate)
└── Warning (Deprecation, Future, User, RuntimeWarning, …)Using them properly
try:
risky()
except (ValueError, KeyError) as e: # multiple types, bound as e
log.warning("bad input: %s", e)
except OSError as e:
if e.errno == 2: ... # inspect errno
raise # re-raise same exception
else:
print("no exception happened") # only on success
finally:
cleanup() # always
raise ValueError(f"bad mode: {mode!r}") from cause # exception chaining
raise TimeoutError("server didn't answer") from None # hide the chain
class InventoryError(Exception):
"""Custom domain error."""
with contextlib.suppress(FileNotFoundError):
os.remove("tmp.txt") # try/except-pass, but readableRules: catch the narrowest exception you can handle; never bare except: (it swallows Ctrl+C); don't use exceptions for normal flow control; log the traceback (logging.exception("msg") inside except).
19. Modules, Packages & Imports
import math # whole module
import numpy as np # aliased
from datetime import date, timedelta # names directly
from pathlib import Path as P # aliased name
from mypkg import submodule # a submodule
from .sibling import helper # relative (inside a package)| Concept | Detail |
|---|---|
| Module | Any .py file; its name is the filename |
| Package | Folder with __init__.py (can be empty); makes import pkg.mod work |
| Search order | script dir → PYTHONPATH → stdlib → site-packages (inspect: sys.path) |
__name__ | "__main__" when run directly, module name when imported — the entry-point idiom |
__all__ = ["a","b"] | What from mod import * gives |
| Executed once | A module's body runs at first import only; later imports reuse the cached sys.modules entry |
| Circular imports | Symptom: ImportError/AttributeError on package start — fix by restructuring or importing inside functions |
| Namespaced | PEP 420: folders without __init__.py are implicit namespace packages |
| Standard layout | src/myproj/… + tests/ + pyproject.toml (see §6) |
if TYPE_CHECKING: | Import type-only deps without runtime cost |
importlib.import_module("x") | Dynamic import by string |
| Reload (rare) | importlib.reload(mod) |
20. Files & I/O
open — all modes
| Mode | Meaning | Notes |
|---|---|---|
r | Read (default) | Missing → FileNotFoundError |
w | Write, truncate | Creates/destroys |
a | Append | Creates |
x | Exclusive create | Exists → FileExistsError |
b / t | Binary / text (default text) | rb, wb, r+b… |
+ | Read+write | r+ no truncate, w+ truncate |
with open("data.txt", "r", encoding="utf-8", newline="\n") as f: # ALWAYS with
text = f.read() # whole file
first = f.readline()
lines = f.readlines() # list of lines
for line in f: # lazy — best for big files
process(line.rstrip("\n"))
with open("out.txt", "w", encoding="utf-8") as f:
f.write("text\n"); f.writelines(lines)with guarantees closing even on exceptions. Always pass encoding= on Windows (default there is locale-dependent — cp1252 landmines). Universal newlines: text mode translates \r\n→\n automatically.
pathlib — every method that matters
from pathlib import Path
p = Path("docs") / "report" / "final.txt" # / joins!| Method | Returns |
|---|---|
Path.cwd() / Path.home() | Where am I / home dir |
p.exists() is_file() is_dir() is_symlink() is_absolute() | Tests |
p.name .stem .suffix .suffixes .parts | 'final.txt', 'final', '.txt', ['docs','report','final.txt'] |
p.parent / p.parents[0..n] | Up one / ancestors |
p.with_name("v2.txt") / p.with_suffix(".md") | Sibling paths |
p.resolve() / p.absolute() | Canonical absolute (symlinks resolved) |
p.expanduser() | ~ expansion |
p.iterdir() | Entries of a folder |
p.glob("*.log") / p.rglob("*.log") | Pattern match (recursive) |
p.mkdir(parents=True, exist_ok=True) | Create (no errors) |
p.rmdir() / p.unlink(missing_ok=True) | Delete dir / file |
p.touch() | Create empty |
p.rename(target) / p.replace(target) | Rename / overwrite-rename |
p.read_text(encoding="utf-8") / p.write_text(s) | Whole-file shuttle |
p.read_bytes() / p.write_bytes(b) | Binary shuttle |
p.open("r") | Built-in open on this path |
p.stat() | .st_size .st_mtime .st_mtime_ns… |
p.chmod(0o644) / p.owner() .group() | Permissions/ownership |
p.samefile(q) / p.is_relative_to(base) / p.relative_to(base) | Relations |
p.as_posix() / p.as_uri() | String forms |
os / os.path / shutil
| Call | Does |
|---|---|
os.getcwd() · os.chdir(p) · os.listdir(p) | Classic trio |
os.environ["KEY"] / os.getenv("K", default) | Environment |
os.system(cmd) † | Shell out — prefer subprocess |
os.path.join(a,b) · os.path.exists/abspath/basename/dirname/splitext/getsize/isfile/isdir | Legacy path ops (pathlib replaces) |
os.remove(f) · os.mkdir/rmdir · os.makedirs(p, exist_ok=True) | File ops |
os.walk(top) | Yields (dirpath, dirnames, filenames) down a tree |
os.stat(f).st_size · os.rename · os.replace | Metadata/moves |
shutil.copy(src,dst) / copy2 (metadata) / copytree / rmtree ⚠ | Copies & deep deletes |
shutil.move(src,dst) | Move |
shutil.disk_usage("/") | Free space |
shutil.make_archive("backup","zip",root_dir) / unpack_archive | Zip/tar without zipfile boilerplate |
shutil.which("python") | Locate executable |
Data formats
import json
json.dumps(obj) / json.dump(obj, f, indent=2, ensure_ascii=False) # → str / file
json.loads(s) / json.load(f) # ← str / file
# tuples→lists, keys must be str, use default= for dates/custom
import csv
with open("t.csv", newline="", encoding="utf-8") as f:
rows = list(csv.DictReader(f)) # dicts keyed by header
with open("out.csv", "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=["a","b"]); w.writeheader(); w.writerows(rows)
import pickle # ⚠ only unpickle data you trust
pickle.dump(obj, open("o.pkl","wb")); obj = pickle.load(open("o.pkl","rb"))
import sqlite3
con = sqlite3.connect("app.db") # or :memory:
con.execute("CREATE TABLE IF NOT EXISTS t (id INTEGER PRIMARY KEY, name TEXT)")
con.execute("INSERT INTO t(name) VALUES (?)", ("alice",)) # ALWAYS ? params
con.commit(); [r for r in con.execute("SELECT * FROM t WHERE name=?", ("alice",))]
con.row_factory = sqlite3.Row; con.close()
import tempfile, io
tmp = tempfile.NamedTemporaryFile(delete=False); tmp.name
buf = io.StringIO("initial"); buf.seek(0) # in-memory text file21. Concurrency
Which model for what?
| Workload | Tool |
|---|---|
| Many network waits (HTTP, DB) | asyncio |
| Many blocking I/O calls (legacy libs, file ops) | threading |
| CPU-bound crunching | multiprocessing (or C extensions/numpy) |
| Don't want to choose | concurrent.futures thread/process pools |
threading
from threading import Thread, Lock, Event, Semaphore, Condition, local
lock = Lock()
def worker(n):
with lock: # GIL still applies to Python bytecode
shared.append(n)
threads = [Thread(target=worker, args=(i,)) for i in range(8)]
[t.start() for t in threads]; [t.join() for t in threads]The GIL: only one thread runs Python bytecode at a time — threads help I/O concurrency, not CPU parallelism (PEP 703 free-threaded build exists experimentally in 3.13).
multiprocessing
from multiprocessing import Process, Pool, Queue, Pipe
def cube(n): return n * n * n
if __name__ == "__main__": # REQUIRED on Windows/macOS spawn
with Pool() as pool:
results = pool.map(cube, range(10)) # parallel map
r = pool.apply_async(cube, (7,)); r.get(timeout=5)
p = Process(target=worker, args=(1,)); p.start(); p.join()Each process = separate interpreter = true parallel CPU work. Costs: pickling, startup. multiprocessing.shared_memory (3.8+) for zero-copy buffers.
concurrent.futures — the friendly face
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, as_completed
with ThreadPoolExecutor(max_workers=20) as ex:
futures = {ex.submit(fetch, url): url for url in urls}
for fut in as_completed(futures):
try: print(fut.result())
except Exception as e: print("failed:", futures[fut], e)
pages = list(ex.map(fetch, urls)) # simple parallel mapasyncio
import asyncio
async def fetch(name, delay):
await asyncio.sleep(delay) # non-blocking wait
return f"{name} done"
async def main():
# sequential
a = await fetch("a", 1)
# concurrent — gather (results in order)
results = await asyncio.gather(fetch("b", 2), fetch("c", 1), return_exceptions=True)
# structured concurrency (3.11+)
async with asyncio.TaskGroup() as tg:
t1 = tg.create_task(fetch("d", 1))
t2 = tg.create_task(fetch("e", 2)) # one fails → all cancelled
# timeout
try:
await asyncio.wait_for(fetch("f", 10), timeout=2)
except TimeoutError: pass
asyncio.run(main()) # THE entry point| API | Purpose |
|---|---|
asyncio.run(coro) | Run the top-level coroutine |
asyncio.gather(*aws) | Concurrent, ordered results |
asyncio.TaskGroup (3.11+) | Structured, auto-cancel on error |
asyncio.create_task(c) | Schedule without awaiting immediately |
asyncio.wait_for(aw, t) / asyncio.timeout(t) (3.11+) | Time limits |
asyncio.sleep(d) | Non-blocking delay |
asyncio.Queue / Lock / Event / Semaphore | Coordination primitives (async versions) |
asyncio.as_completed(aws) | Iterate as they finish |
asyncio.to_thread(func, …) | Push blocking call to a thread |
asyncio.start_server / open_connection | Async sockets |
Libraries must be async-native (aiohttp, httpx async mode). await only inside async def. One event loop per process.
22. Standard Library Essentials
datetime — the complete kit
from datetime import datetime, date, time, timedelta, timezone
now = datetime.now() # local, naive
utc = datetime.now(timezone.utc) # TIMEZONE-AWARE — prefer for real code
datetime.fromisoformat("2026-08-30T14:30:00+02:00") # parse ISO (3.11+: most formats)
dt = datetime.strptime("30/08/2026", "%d/%m/%Y") # parse any format
dt.strftime("%Y-%m-%d %H:%M") # format out
dt + timedelta(days=7, hours=3) # arithmetic
(dt2 - dt1).days / .total_seconds() # differences
dt.timestamp() ; datetime.fromtimestamp(1760000000) # epoch shuttle
date.today() ; dt.date() ; dt.year .month .day .hour .weekday() # Monday=0 / isoweekday()strftime directives: %Y %m %d %H %M %S big four · %y 2-digit year · %I %p 12-hour + AM/PM · %j day of year · %a %A %b %B names · %f microseconds · %z %Z timezone · %s† · %U %W %V weeks · %c %x %X locale · %% literal. Gotcha: comparing naive and aware datetimes raises TypeError — normalize with dt.astimezone(). Third-party dateparser/pendulum for human formats ("3 days ago").
collections
| Tool | Use |
|---|---|
Counter("mississippi").most_common(3) | Frequency counting |
defaultdict(list) | Dict with auto-factory — kills setdefault boilerplate |
deque(maxlen=n) | Fast append/pop both ends — sliding windows, queues |
namedtuple("P", "x y") | Tuple with names |
OrderedDict † | Ordered since 3.7 plain dict; still useful for move_to_end |
ChainMap(a, b) | Layered lookup (defaults over overrides) |
UserDict/List/String | Base classes for custom containers |
random, math, statistics, secrets
| Call | Returns |
|---|---|
random.random() / randint(1,6) / randrange(0,10,2) | Float [0,1) / inclusive int / step |
random.choice(seq) / choices(seq, k=5, weights=…) / sample(seq, k) | Picks (no/with replacement) |
random.shuffle(list) | In-place |
random.seed(42) | Reproducible |
math.ceil floor fabs factorial gcd lcm sqrt cbrt(3.11+) | Basics |
math.pi e tau inf nan · math.log(x, b) log2 log10 exp | Constants/logs |
math.isclose(a,b,rel_tol=1e-9) | Float-safe compare |
statistics.mean median mode stdev variance quantiles | Stats |
secrets.token_hex(32) / token_urlsafe(16) / randbelow(n) | Crypto-safe randomness |
secrets.compare_digest(a,b) | Constant-time compare |
⚠ random is NOT for security — use secrets. Random ints in hot loops: random.Random(seed) instance avoids global lock.
Hashing & encoding
| Call | Purpose |
|---|---|
hashlib.sha256(b).hexdigest() (md5† sha1† sha512 blake2b sha3_256) | Digests — verify downloads, fingerprint |
hashlib.file_digest(open(f,'rb'), 'sha256') (3.11+) | Streamed file hash |
hmac.new(key, msg, 'sha256').hexdigest() | Signed messages |
base64.b64encode(b) / b64decode / urlsafe_b64* | Transport encoding (not encryption!) |
uuid.uuid4() | Random UUID; uuid1() host-based; uuid5(ns, name) deterministic |
subprocess — run anything
import subprocess
r = subprocess.run(["ping", "-n", "1", "example.com"], capture_output=True,
text=True, timeout=10, check=False)
r.returncode, r.stdout, r.stderr
subprocess.run("echo hi > f.txt", shell=True) # ⚠ shell=True = injection risk
subprocess.run(["python", "script.py"], input="y\n", text=True, check=True)| Param | Meaning |
|---|---|
args list | Program + args (no shell parsing — safe) |
capture_output=True | Collect stdout+stderr |
text=True | Decode to str (else bytes) |
input="…" | Feed stdin |
check=True | Non-zero → CalledProcessError |
timeout=30 | Kill after 30 s (TimeoutExpired) |
cwd=, env={**os.environ, "K":"v"} | Working dir / environment |
Popen(…) | Low-level streaming (p.stdout.readline(), p.communicate()) |
Legacy: os.system † · os.popen † — migrate both to subprocess.
argparse — full walkthrough
import argparse
p = argparse.ArgumentParser(
prog="tool", description="Do the thing",
epilog="Enjoy.")
p.add_argument("src", help="source file") # positional
p.add_argument("dst", nargs="?", default="out.txt") # optional positional
p.add_argument("-n", "--dry-run", action="store_true") # flag
p.add_argument("-v", "--verbose", action="count", default=0) # -vvv → 3
p.add_argument("--limit", type=int, default=10) # typed option
p.add_argument("--mode", choices=["fast","safe"], default="safe")
p.add_argument("--tags", action="append") # repeatable → list
p.add_argument("--log", type=argparse.FileType("w")) # opens the file!
p.add_argument("--retries", metavar="N", type=int, default=3)
sub = p.add_subparsers(dest="cmd", required=True)
p_init = sub.add_parser("init", help="create config")
p_init.add_argument("--force", action="store_true")
p_push = sub.add_parser("push", help="upload")
p_push.add_argument("target")
args = p.parse_args() # --help auto-generated from all of the above| Concept | Detail |
|---|---|
nargs | N exactly · ? 0-or-1 · * any list · + at least one |
action | store · store_true/false/const · append · count · extend · custom Action class |
type | Any callable — int, float, Path, datetime.fromisoformat |
default + required=True | Optionality control |
dest | Attribute name (from long option by default) |
parse_known_args() | Ignore unknown (wrapper scripts) |
args = p.parse_args(["init","--force"]) | Testing parsers programmatically |
Alternatives: typer (type-hint driven, pretty) and click (decorator based) — argparse needs zero dependencies.
logging — full walkthrough
import logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s %(levelname)-8s %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S",
filename="app.log", # omit → stderr
)
log = logging.getLogger("payroll") # per-module loggers: logging.getLogger(__name__)
log.debug("detail %s", item) # lazy % formatting — never f-strings in hot paths
log.info("processed %d rows", n)
log.warning("deprecated call")
log.error("failed: %s", err)
log.exception("boom") # ERROR + traceback (in except block)| Level | Numeric | Use |
|---|---|---|
DEBUG | 10 | Everything while developing |
INFO | 20 | Normal milestones |
WARNING | 30 | Default level — surprising but recoverable |
ERROR | 40 | Operation failed |
CRITICAL | 50 | App going down |
Advanced: handlers (StreamHandler, FileHandler, RotatingFileHandler(maxBytes, backupCount), SMTPHandler), Formatter, dictConfig from YAML/JSON, logging.config.dictConfig({...}), propagate/filter per logger. Rule: libraries log, never configure basicConfig inside a library — only in the app entry point.
Networking quick hits
| Call | Purpose |
|---|---|
urllib.request.urlopen(url) | Stdlib HTTP GET (clunky but zero-dep) |
pip install requests → requests.get(url, timeout=5, headers=…); r.json() r.status_code r.raise_for_status() | The human API |
httpx | requests-compatible + async + HTTP/2 |
socket.create_connection((host, port), timeout=5) | Raw TCP |
email/smtplib | MIME building + SMTP send (smtplib.SMTP(host).send_message(msg)) |
webbrowser.open(url) | Open browser |
Archives, glob, platform
| Call | Purpose |
|---|---|
zipfile.ZipFile("a.zip") → .namelist() .extractall(dir) .read(name) · ZipFile("a.zip","w").write(f) | Zip I/O |
tarfile.open("a.tar.gz", "r:gz") | Tar I/O |
glob.glob("data/*.csv", recursive=True) · glob.iglob | Wildcard files (pathlib glob usually nicer) |
platform.system() .machine() .python_version() | OS/CPU/Py detection |
os.name ("nt"/"posix") · sys.platform (win32/linux/darwin) | Platform branches |
sys.argv · sys.exit(code) · sys.stdin/stdout/stderr · sys.modules · sys.path | System handles |
time.time() time.sleep(s) time.perf_counter() | Clocks — perf_counter for timing! |
shelve.open("data") | Pickle-backed dict on disk |
PART 3 — HANDS-ON TUTORIALS
T1. Your First Hour
Goal: variables → data → loops → functions, ending with two tiny real programs.
# Step 1 — the calculator soul of Python (REPL: >>> means type it live)
2 + 2, 7 / 2, 7 // 2, 7 % 2, 2 ** 10 # (4, 3.5, 3, 1, 1024)
# Step 2 — variables & types (dynamic: names are labels, not boxes)
width, height = 3, 4
area = width * height
type(area) # <class 'int'>
# Step 3 — strings & f-strings
name = "Ada"
print(f"{name} computed area={area}")
# Step 4 — lists and loops
temps = [21, 25, 19, 30, 27]
for t in temps:
print("hot" if t >= 25 else "mild", t)
print("avg:", sum(temps) / len(temps))
# Step 5 — functions
def to_f(c): return c * 9 / 5 + 32
print([to_f(t) for t in temps])
# Step 6 — dict as a tiny database
people = {"ada": 36, "alan": 41}
people["grace"] = 45
for who, age in sorted(people.items()):
print(f"{who:>6}: {age}")
# Step 7 — program 1: number guessing game
import random
secret = random.randint(1, 100)
while (guess := int(input("Guess: "))) != secret:
print("higher" if guess < secret else "lower")
print("Correct!")
# Step 8 — program 2: word counter
text = input("Sentence: ")
counts = {}
for word in text.lower().split():
counts[word] = counts.get(word, 0) + 1
print(sorted(counts.items(), key=lambda kv: -kv[1]))Checkpoint: you can store data (list/dict), repeat work (for/while), package logic (def), and read input. That's 80 % of daily Python.
T2. Parse a Log File → CSV Report
# parse access logs like: 2026-08-30 12:01:02 ERROR payment failed id=771
import re, csv
from collections import Counter
from pathlib import Path
log_re = re.compile(r"(?P<date>\d{4}-\d{2}-\d{2}) (?P<time>\d{2}:\d{2}:\d{2}) "
r"(?P<level>INFO|WARNING|ERROR) (?P<msg>.*)")
rows, level_counts = [], Counter()
for line in Path("app.log").read_text(encoding="utf-8").splitlines():
if m := log_re.match(line):
rows.append(m.groupdict())
level_counts[m["level"]] += 1
with open("report.csv", "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=["date", "time", "level", "msg"])
w.writeheader(); w.writerows(rows)
print("levels:", dict(level_counts))
print("errors on:", {r["date"] for r in rows if r["level"] == "ERROR"})Teaches: re named groups, walrus, Counter, pathlib, csv.DictWriter.
T3. Consume Any REST API (JSON)
import requests
def get_json(url, **params):
r = requests.get(url, params=params, timeout=10)
r.raise_for_status()
return r.json()
data = get_json("https://api.github.com/repos/python/cpython/events", per_page=5)
for ev in data:
print(f"{ev['type']:<22} {ev['actor']['login']:<20} {ev['repo']['name']}")
# POST with auth:
# requests.post(url, json={"key": "value"}, headers={"Authorization": f"Bearer {token}"})Teaches: HTTP verbs, params/timeout, raise_for_status, JSON navigation (['type']), and the habit of wrapping API calls in small functions. Retry logic: HTTPAdapter(max_retries=3) in a requests.Session.
T4. Build a Real CLI App (argparse)
"""todo.py — a tiny task manager."""
import argparse, json
from pathlib import Path
DB = Path.home() / ".todo.json"
def load():
return json.loads(DB.read_text()) if DB.exists() else []
def save(tasks): DB.write_text(json.dumps(tasks, indent=2))
def main():
p = argparse.ArgumentParser(description="Tiny todo")
sub = p.add_subparsers(dest="cmd", required=True)
a = sub.add_parser("add"); a.add_argument("task", help="task text")
d = sub.add_parser("done"); d.add_argument("n", type=int, help="task number")
sub.add_parser("list", help="show tasks")
args = p.parse_args()
tasks = load()
match args.cmd:
case "add": tasks.append({"task": args.task, "done": False}); save(tasks)
case "done": tasks[args.n - 1]["done"] = True; save(tasks)
case "list":
for i, t in enumerate(tasks, 1):
print(f"{'✓' if t['done'] else '·'} {i}. {t['task']}")
if __name__ == "__main__":
main()Run: python todo.py add "Ship the report" → python todo.py list → python todo.py done 1. Next level: convert with typer (add type hints, get --help for free) and install as a command with pipx install after adding a [project.scripts] entry (§6).
T5. OOP with Dataclasses — an Inventory Manager
from dataclasses import dataclass, field
from enum import Enum
class Condition(Enum):
NEW = "new"; USED = "used"; BROKEN = "broken"
@dataclass(order=True)
class Item:
sort_index: float = field(init=False, repr=False)
sku: str = ""
name: str = ""
price: float = 0.0
condition: Condition = Condition.NEW
tags: list[str] = field(default_factory=list)
def __post_init__(self):
self.sort_index = -self.price # highest price first
class Inventory:
def __init__(self): self._items: dict[str, Item] = {}
def add(self, item: Item): self._items[item.sku] = item
def total_value(self) -> float:
return sum(i.price for i in self._items.values() if i.condition != Condition.BROKEN)
def find(self, query: str) -> list[Item]:
q = query.lower()
return [i for i in self._items.values() if q in i.name.lower() or q in " ".join(i.tags)]
inv = Inventory()
inv.add(Item(sku="A1", name="Keyboard", price=79.9, tags=["input"]))
inv.add(Item(sku="A2", name="Monitor", price=349, condition=Condition.USED))
print(inv.total_value(), inv.find("monitor"))Teaches: @dataclass(order=True), field(init=False) + __post_init__, Enum, private-by-convention attributes, type-hinted composition.
T6. Package & Publish a Library to PyPI
# 1. Layout
mylib/
├── pyproject.toml # from §6 — name, version, deps, [project.scripts]
├── README.md
├── src/mylib/__init__.py
└── tests/test_mylib.py
# 2. Local sanity
python -m venv .venv && source .venv/bin/activate
pip install -e . && mylib-cli --help # entry point works?
# 3. Build & validate
pip install build twine
python -m build
twine check dist/*
# 4. Rehearse on TestPyPI, then the real thing
twine upload --repository testpypi dist/*
pip install -i https://test.pypi.org/simple/ mylib && python -c "import mylib"
twine upload dist/* # needs a PyPI API tokenChecklist before upload: unique name, version bumped, README renders (twine check), license present, no secrets in the sdist. Every re-upload of the same version is rejected — bump first.
T7. Test Like a Pro with pytest
# tests/test_math.py
import pytest
from mylib import add, divide, load_config
def test_add_ints():
assert add(2, 3) == 5
@pytest.mark.parametrize("a,b,out", [(0,0,0), (-1,1,0), (100,1,101)])
def test_add_many(a, b, out):
assert add(a, b) == out
def test_divide_by_zero():
with pytest.raises(ZeroDivisionError):
divide(1, 0)
@pytest.fixture
def config(tmp_path):
cfg = tmp_path / "c.ini"; cfg.write_text("[main]\nkey=1\n")
return load_config(cfg)
def test_config(config):
assert config["main"]["key"] == "1"
def test_output(capsys):
print("hi"); assert capsys.readouterr().out == "hi\n"pytest -v --durations=3
pytest --lf
pytest --cov=mylib --cov-report=term-missingTeaches: one concept per test, AAA (Arrange-Act-Assert), fixtures over setup boilerplate, parametrize over copy-paste, raises-testing, coverage flags.
T8. Automate the Boring Stuff
# A — rename & sort a folder of photos by EXIF-less mtime
from pathlib import Path
from datetime import datetime
for p in Path("dump").iterdir():
if p.is_file():
stamp = datetime.fromtimestamp(p.stat().st_mtime).strftime("%Y-%m/%Y%m%d_%H%M%S")
target = Path("sorted") / f"{stamp}{p.suffix}"
target.parent.mkdir(parents=True, exist_ok=True)
p.rename(target)
# B — Excel → cleaned Excel (pip install openpyxl pandas)
import pandas as pd
df = pd.read_excel("sales.xlsx", sheet_name="Q3")
clean = (df.dropna(subset=["amount"])
.assign(total=lambda d: d.amount * d.qty)
.groupby("region", as_index=False)["total"].sum())
clean.to_excel("sales_summary.xlsx", index=False)
# C — send an email report (stdlib)
import smtplib
from email.message import EmailMessage
msg = EmailMessage()
msg["From"], msg["To"], msg["Subject"] = "me@x.com", "boss@x.com", "Weekly report"
msg.set_content(open("report.txt").read())
with smtplib.SMTP("smtp.example.com", 587) as s:
s.starttls(); s.login("me@x.com", "app-password"); s.send_message(msg)
# D — watch a folder for new files
import time
seen = set()
while True:
for p in Path("inbox").glob("*.csv"):
if p not in seen:
seen.add(p); print("new:", p); # process(p)
time.sleep(2)T9. Async — a Concurrent Downloader
import asyncio, aiohttp, time
URLS = [f"https://httpbin.org/delay/{d}" for d in (1, 1, 1, 1, 1)]
async def fetch(session, url):
async with session.get(url, timeout=aiohttp.ClientTimeout(total=15)) as r:
return url, r.status
async def main():
async with aiohttp.ClientSession() as session:
async with asyncio.TaskGroup() as tg: # 3.11+
tasks = [tg.create_task(fetch(session, u)) for u in URLS]
for t in tasks:
print(t.result())
start = time.perf_counter()
asyncio.run(main())
print(f"5 requests in {time.perf_counter() - start:.1f}s (sequential would be ~5s)")pip install aiohttp first. Same pattern with threads for blocking libraries: ThreadPoolExecutor + fetch (§21). Teaches: async/await, sessions, TaskGroup, timeouts, and measuring speedups honestly.
T10. Debug & Profile Like a Detective
# 1 — reproduce under pdb
python -m pdb app.py input.csv
(Pdb) b app.py:42 # break where it goes wrong
(Pdb) c # run to it
(Pdb) p rows[:3] # inspect
(Pdb) pp {r["id"]: r for r in rows}
(Pdb) w # how did we get here?
(Pdb) interact # full REPL with all locals
# 2 — or post-mortem the last crash
python -i app.py
>>> import pdb; pdb.pm()
# 3 — find the hotspot
python -m cProfile -s cumtime app.py | head -25
# or: python -X importtime -c "import app" 2>&1 | sort -t'|' -k2 -n | tailMethod: (1) make it fail deterministically (smallest input), (2) read the traceback bottom-up — last frame is where it died, walk up for who caused it, (3) inspect state with pdb rather than guessing, (4) profile before optimizing, (5) after fixing: add the regression test (T7).
Library Tutorials (L1–L10) — the essential packages, hands-on
The ten tutorials below teach the libraries shipped in this repo's requirements.txt — the same code, the same style as T1–T10. Set up once, then do them in any order: ```bash python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate python -m pip install -r requirements.txt # installs every library used below ```
L1. requests & httpx — HTTP Like a Human
Goal: fetch pages and APIs, handle auth, sessions, errors and retries — the right way.
import requests
# 1 — the basic GET (ALWAYS pass a timeout)
r = requests.get("https://api.github.com/users/torvalds", timeout=10)
r.raise_for_status() # 4xx/5xx → exception instead of silent garbage
print(r.status_code, r.headers["content-type"])
bio = r.json() # parse the body as JSON → dict
# 2 — query params, headers, auth
r = requests.get("https://api.github.com/search/repositories",
params={"q": "python cli", "sort": "stars", "per_page": 3},
headers={"Accept": "application/vnd.github+json"},
timeout=10)
for item in r.json()["items"]:
print(f"{item['full_name']:40} ★{item['stargazers_count']}")
# 3 — POST with a JSON body (what 95 % of APIs want)
created = requests.post("https://httpbin.org/post",
json={"title": "hello", "done": False}, timeout=10)
# 4 — Session: connection reuse + persistent cookies + one place for defaults
with requests.Session() as s:
s.headers.update({"Authorization": "Bearer YOUR_TOKEN", "User-Agent": "learn/1.0"})
s.get("https://api.github.com/user", timeout=10) # authenticated!
for page in range(1, 3): # pagination
resp = s.get("https://api.github.com/users/torvalds/repos",
params={"page": page}, timeout=10)
repos = resp.json()
if not repos: break
print([repo["name"] for repo in repos])Error handling pattern (copy this into every project):
try:
r = requests.get(url, timeout=10)
r.raise_for_status()
except requests.Timeout:
print("server too slow")
except requests.HTTPError as e:
print(f"HTTP {e.response.status_code} — check the URL/token")
except requests.ConnectionError:
print("no network / DNS fail")
else:
data = r.json()httpx — same API, plus async and HTTP/2:
import httpx
r = httpx.get("https://example.org", timeout=10) # drop-in for requests
import asyncio
async def fetch_all(urls):
async with httpx.AsyncClient(timeout=10) as client: # concurrent fetches
replies = await asyncio.gather(*(client.get(u) for u in urls))
return [r.status_code for r in replies]
print(asyncio.run(fetch_all(["https://example.org"] * 5)))Checkpoint: you can talk to any REST API with params, auth, sessions and proper errors. Exercise: fetch your own GitHub profile and print follower count; then list the first page of your repos.
L2. pydantic — Data That Validates Itself
Goal: stop hand-checking dicts — declare what data should look like and let pydantic enforce it.
from pydantic import BaseModel, Field, field_validator, model_validator
from datetime import date
from typing import Literal
class Address(BaseModel): # nested model
city: str
zip_code: str = Field(pattern=r"^\d{4,10}$") # regex-constrained
class User(BaseModel):
id: int
name: str = Field(min_length=1, max_length=60)
email: str # basic type checks
role: Literal["admin", "user"] = "user" # only these values allowed
birthday: date | None = None
address: Address
@field_validator("email") # custom rule
@classmethod
def email_lowercase(cls, v): return v.lower()
@model_validator(mode="after") # cross-field rule
def check(self):
if self.role == "admin" and self.name.lower() == "guest":
raise ValueError("admin cannot be named guest")
return selfWatch it work:
u = User.model_validate({ # messy input → validated object
"id": "7", # str → coerced to int!
"name": "Matin",
"email": "MATIN@Example.COM",
"address": {"city": "Vienna", "zip_code": "1010"},
})
print(u.id, u.email, u.address.city) # 7 matin@example.com Vienna
print(u.model_dump()) # back to a plain dict
print(User.model_json_schema()) # a JSON Schema, for free
from pydantic import ValidationError
try:
User(id=1, name="x", email="nope", address={"city": "c", "zip_code": "ABC"})
except ValidationError as e:
print(e.error_count(), "errors") # precise, structured error reportConfig with pydantic-settings + .env (the clean way to read environment variables):
# .env file: API_TOKEN=abc123 DEBUG=true
from pydantic_settings import BaseSettings
from pydantic import Field
class Settings(BaseSettings):
api_token: str = Field(repr=False) # repr=False hides it from logs
debug: bool = False
model_config = {"env_file": ".env"} # reads .env automatically
settings = Settings()
print(settings.debug) # True — typed, validated configWhere it shines: validating API responses (User.model_validate(resp.json())), FastAPI request bodies, and typed settings. Checkpoint: write a Product model with price > 0, stock ≥ 0, and a category from a fixed set.
L3. numpy — Think in Arrays
Goal: replace Python loops with vectorized array math — the foundation under pandas, ML, and image processing.
import numpy as np
# 1 — creation
a = np.array([1, 2, 3, 4]) # from a list
z = np.zeros(5); ones = np.ones((2, 3)) # shapes: (rows, cols)
rng = np.random.default_rng(seed=42)
noise = rng.normal(0, 1, size=1_000_000) # a million samples, instantly
grid = np.arange(12).reshape(3, 4) # 0..11 in a 3×4 matrix
# 2 — elementwise math WITHOUT loops (this is the whole point)
temps_c = np.array([12.5, 18.0, 24.3, 31.2, 27.8])
temps_f = temps_c * 9 / 5 + 32 # one line, C speed
print(temps_f.round(1))
# 3 — slicing returns VIEWS (no copy!) — 2D: [rows, cols]
grid[0, 1] # row 0, col 1
grid[:, 0] # entire first column
grid[1:, ::2] # rows 1+, every 2nd column
grid[grid > 5] # boolean mask → 1D of matches
np.where(temps_c > 25, "hot", "mild") # vectorized conditional
# 4 — aggregations and the axis argument
grid.sum(), grid.mean(), grid.std()
grid.sum(axis=0) # down the columns → per-column totals
grid.sum(axis=1) # across the rows → per-row totals
np.argmax(temps_c) # index of the max
# 5 — broadcasting: combine different shapes sensibly
prices = np.array([10.0, 20.0, 30.0]) # shape (3,)
quantities = np.array([[1, 2, 3], [4, 5, 6]]) # shape (2, 3)
totals = prices * quantities # (3,) stretches to (2, 3) — row-wise
print(totals.sum(axis=1)) # per-order totalsWhy it matters: a vectorized numpy operation is typically 10–100× faster than the equivalent Python for loop. Rule of thumb — if you wrote a loop over numbers, there's a numpy one-liner.
# before # after
out = [] import numpy as np
for x in data: out = np.sqrt(np.abs(data))
out.append(abs(x)**0.5)Checkpoint: generate 100,000 random exam scores (mean 65, std 12), clip them to 0–100 (np.clip), and print the mean, median (np.median), and the percentage ≥ 50.
L4. pandas — DataFrames End to End
Goal: load messy tabular data, clean it, interrogate it, export it — the complete workflow on one realistic file.
import pandas as pd
# 1 — load (csv / excel / json / sql all work)
df = pd.read_csv("sales.csv", parse_dates=["date"])
# 2 — inspect before touching anything
df.head() # first rows
df.shape # (rows, cols)
df.info() # dtypes + missing-value counts
df.describe() # numeric summary
# 3 — select: columns, rows, filters
df[["region", "amount"]] # column subset
df.loc[df["amount"] > 500, ["region", "amount"]] # boolean rows + column subset
df.iloc[0:10] # positional slice
df.query("region == 'East' and amount > 200") # SQL-ish syntax
# 4 — clean: missing values and broken types
df["amount"] = pd.to_numeric(df["amount"], errors="coerce") # bad values → NaN
df = df.dropna(subset=["amount"]) # drop rows without an amount
df["region"] = df["region"].str.strip().str.title() # normalize text
# 5 — derive columns
df["month"] = df["date"].dt.to_period("M")
df["amount_with_tax"] = df["amount"] * 1.2
df["size"] = pd.cut(df["amount"], bins=[0, 100, 500, np.inf],
labels=["small", "medium", "large"])
# 6 — the groupby reflex: split → apply → combine
df.groupby("region")["amount"].agg(["count", "sum", "mean"])
df.groupby(["region", "month"])["amount"].sum().unstack() # pivot wide
df.pivot_table(index="region", columns="size", values="amount",
aggfunc="sum", fill_value=0)
# 7 — combine frames
pd.merge(orders, customers, on="customer_id", how="left") # SQL join
pd.concat([q1, q2, q3]) # stack frames
# 8 — export
df.to_csv("clean.csv", index=False)
df.to_excel("report.xlsx", sheet_name="sales")
df.to_sql("sales", engine, if_exists="replace") # into a databaseRealistic mini-analysis (run this on any CSV):
top = (df.groupby("region")["amount"]
.sum()
.sort_values(ascending=False))
print(top.head(10))
growth = df.set_index("date").resample("ME")["amount"].sum() # monthly trend
print(growth.pct_change().round(3).tail()) # % changePerformance habits: use pd.read_csv(..., chunksize=100_000) for huge files; df["cat"] = df["cat"].astype("category") for repeated strings; and know that polars (Appendix H) is the fast alternative for very large data.
Checkpoint: load any CSV, find the numeric column, and produce a per-category summary table sorted by total. Exercise: which month had the biggest jump over the previous month?
L5. matplotlib — Charts That Explain Themselves
Goal: the eight charts you'll actually use, with proper labels, and saving to files.
import matplotlib.pyplot as plt
import numpy as np
# 0 — the anatomy: figure = the canvas, axes = one chart
fig, ax = plt.subplots(figsize=(8, 4)) # one axes
x = np.linspace(0, 10, 100)
# 1 — line (trends over ordered x)
ax.plot(x, np.sin(x), label="sin", linewidth=2)
ax.plot(x, np.cos(x), label="cos", linestyle="--")
ax.set_title("Trig functions") # ALWAYS title + label axes
ax.set_xlabel("x"); ax.set_ylabel("y")
ax.legend(); ax.grid(alpha=0.3)
fig.savefig("trig.png", dpi=150, bbox_inches="tight") # save BEFORE show
plt.show()The chart menu — pick by question:
fig, axes = plt.subplots(2, 2, figsize=(10, 8)) # 2×2 grid of axes
ax = axes[0, 0]
ax.bar(["A", "B", "C"], [3, 7, 5]) # compare categories
ax.set_title("bar — comparison")
axes[0, 1].scatter(rng.normal(size=50), rng.normal(size=50),
alpha=0.7) # relationship between 2 vars
axes[0, 1].set_title("scatter — relationship")
axes[1, 0].hist(rng.normal(0, 1, 1000), bins=30) # distribution of one var
axes[1, 1].set_title("hist — distribution")
data = [rng.normal(0, 1, 100), rng.normal(2, 1, 100)]
axes[1, 1].boxplot(data) # spread + outliers
axes[1, 1].set_title("box — spread")
fig.tight_layout()
fig.savefig("dashboard.png", dpi=150)Daily-driver tricks:
plt.style.use("seaborn-v0_8-darkgrid") # instant decent look (plt.style.available)
df.plot(kind="line", x="date", y="amount", title="Sales") # pandas one-liner!
df.groupby("region")["amount"].sum().plot(kind="barh") # groupby → chart
fig, ax = plt.subplots()
im = ax.imshow(np.random.random((5, 5)), cmap="viridis") # heatmap
fig.colorbar(im)Checkpoint: plot the monthly-totals Series you made in L4 as a line chart, and the per-region totals as a horizontal bar chart — with titles, labels, and a saved PNG.
L6. typer + rich — Beautiful Command-Line Apps
Goal: build a real CLI tool with typed arguments, colored tables and progress — in 30 lines.
"""notes.py — typer + rich demo CLI"""
from pathlib import Path
import json, typer
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
app = typer.Typer(help="Tiny note manager")
DB = Path.home() / ".notes.json"
console = Console()
def load() -> list[dict]:
return json.loads(DB.read_text()) if DB.exists() else []
@app.command()
def add(text: str, tag: str = typer.Option("general", help="category tag")):
"""Add a note."""
notes = load()
notes.append({"text": text, "tag": tag})
DB.write_text(json.dumps(notes, indent=2))
console.print(Panel(f"[green]Saved[/] '{text}' [dim]({tag})[/]"))
@app.command()
def list(tag: str = typer.Option(None, help="filter by tag"),
limit: int = typer.Option(10, min=1, max=100)):
"""List notes as a rich table."""
notes = [n for n in load() if tag is None or n["tag"] == tag][:limit]
table = Table(title="Notes")
table.add_column("#", justify="right", style="cyan")
table.add_column("Tag", style="magenta")
table.add_column("Text")
for i, n in enumerate(notes, 1):
table.add_row(str(i), n["tag"], n["text"])
console.print(table)
@app.command()
def stats():
"""Counts per tag, with a progress bar for show."""
from collections import Counter
with console.status("counting..."):
counts = Counter(n["tag"] for n in load())
for tag, count in counts.most_common():
console.print(f"[bold]{tag}[/]: {count}")
if __name__ == "__main__":
app()python notes.py add "ship the report" --tag work
python notes.py add "buy milk"
python notes.py list --tag work
python notes.py --help # typer wrote all this help for youWhy typer wins: type hints become validation (limit: int = typer.Option(10, min=1, max=100) rejects bad input with a clear error), and --help documents itself. Why rich: tables, panels, colors, rich.progress.Progress for long loops, Console().log() for pretty debugging.
Checkpoint: add a clear command that empties the database (ask typer.confirm("Sure?") first).
L7. SQLAlchemy — A Real Database in 40 Lines
Goal: define tables as classes, create the database, insert, query — the modern 2.0 style.
from sqlalchemy import create_engine, select, ForeignKey
from sqlalchemy.orm import (DeclarativeBase, Mapped, mapped_column,
Session, relationship)
class Base(DeclarativeBase): pass
class Bookmark(Base):
__tablename__ = "bookmarks"
id: Mapped[int] = mapped_column(primary_key=True)
url: Mapped[str]
title: Mapped[str]
tags: Mapped[list["Tag"]] = relationship(back_populates="bookmark")
class Tag(Base):
__tablename__ = "tags"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str]
bookmark_id: Mapped[int] = mapped_column(ForeignKey("bookmarks.id"))
bookmark: Mapped[Bookmark] = relationship(back_populates="tags")
engine = create_engine("sqlite:///bookmarks.db") # just a file; use
Base.metadata.create_all(engine) # postgresql://… for real DBs
with Session(engine) as session: # CREATE
session.add_all([
Bookmark(url="https://docs.python.org", title="Python docs",
tags=[Tag(name="docs"), Tag(name="daily")]),
Bookmark(url="https://pypi.org", title="PyPI", tags=[Tag(name="docs")]),
])
session.commit()
with Session(engine) as session: # READ
for bm in session.scalars(select(Bookmark).where(Bookmark.url.like("%py%"))):
print(bm.title, [t.name for t in bm.tags])
stmt = (select(Bookmark) # UPDATE + DELETE
.where(Bookmark.title == "PyPI"))
session.scalars(stmt).one().title = "Python Package Index"
session.commit()The patterns you'll reuse forever: engine (one per app) → Session (short-lived, per task) → select() queries with .where/.order_by/.limit → session.commit(). For schema changes later, that's Alembic (Appendix H). Pandas bridge: pd.read_sql(select(Bookmark), engine).
Checkpoint: add a visited: Mapped[bool] column and a query that counts unvisited bookmarks.
L8. BeautifulSoup + lxml — Scraping Politely
Goal: download a page, extract structured data, and respect the site while doing it.
import time
import requests
from bs4 import BeautifulSoup
def get_soup(url):
r = requests.get(url, timeout=10,
headers={"User-Agent": "learning-bot/1.0"}) # identify yourself
r.raise_for_status()
return BeautifulSoup(r.text, "lxml") # lxml parser = fast
soup = get_soup("https://quotes.toscrape.com/") # a site built for practice
# 1 — find vs select: pick your weapon
first_quote = soup.find("div", class_="quote") # first match by tag+class
all_quotes = soup.find_all("div", class_="quote") # all matches
top_tags = soup.select(".tag-item .tag") # CSS selectors (most flexible)
# 2 — dig into one element
for q in all_quotes:
text = q.select_one(".text").get_text(strip=True)
author = q.select_one(".author").get_text()
tags = [t.get_text() for t in q.select(".tag")]
print(f"{author}: {text[:50]}… {tags}")
# 3 — attributes, links, tables
link = soup.select_one("a")["href"] # attribute access
table_rows = soup.select("table tr") # any table → rows
import pandas as pd
# df = pd.read_html(r.text)[0] # tables in ONE line
# 4 — follow pagination like a courteous visitor
page = 1
while True:
soup = get_soup(f"https://quotes.toscrape.com/page/{page}/")
quotes = soup.select("div.quote")
if not quotes: break
print(f"page {page}: {len(quotes)} quotes")
page += 1
time.sleep(1) # rate-limit yourself — alwaysScraping etiquette: check robots.txt, keep a delay between requests, cache pages while developing (r.text to a file), prefer an official API or RSS when one exists, and set an honest User-Agent. Use scrapy (Appendix H) when a site grows into a real crawling project.
Checkpoint: scrape 5 pages of quotes and build a DataFrame of (author, text, tags) — then count quotes per author (hello again, L4).
L9. Pillow + openpyxl — Images & Excel Reports
Goal: batch-process a photo folder and write a real styled Excel report about it.
from pathlib import Path
from PIL import Image
# 1 — open · inspect · save (format conversion is just save-as)
img = Image.open("photo.jpg")
print(img.size, img.mode) # (width, height) e.g. (4032, 3024) RGB
img.save("photo.png")
# 2 — the everyday transforms
img.resize((800, 600)) # exact size (distorts)
img.crop((100, 100, 500, 500)) # (left, top, right, bottom)
img.rotate(90, expand=True)
img.convert("L") # grayscale
img.filter(Image.Filter.BLUR)
# 3 — thumbnail: fits the box, KEEPS the aspect ratio — use this for web images
img.thumbnail((300, 300))
# 4 — batch a whole folder
src, out = Path("photos"), Path("photos/thumbs")
out.mkdir(exist_ok=True)
records = []
for file in sorted(src.glob("*.jpg")):
with Image.open(file) as im:
im.thumbnail((300, 300))
thumb = out / f"{file.stem}_thumb.jpg"
im.convert("RGB").save(thumb, "JPEG", quality=85)
records.append((file.name, *im.size, thumb.name))Now report it in Excel with openpyxl:
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill
from openpyxl.utils import get_column_letter
wb = Workbook()
ws = wb.active
ws.title = "Thumbnails"
header_font = Font(bold=True, color="FFFFFF")
header_fill = PatternFill("solid", fgColor="2F7DAE")
for col, title in enumerate(["Original", "Width", "Height", "Thumbnail"], 1):
c = ws.cell(row=1, column=col, value=title)
c.font, c.fill = header_font, header_fill
for row, rec in enumerate(records, 2):
for col, value in enumerate(rec, 1):
ws.cell(row=row, column=col, value=value)
for col, width in enumerate([28, 10, 10, 28], 1): # readable columns
ws.column_dimensions[get_column_letter(col)].width = width
ws.freeze_panes = "A2" # keep the header visible
ws.cell(row=len(records) + 3, column=1,
value=f"=COUNTA(A2:A{len(records) + 1})").comment = None # real formula
wb.save("thumbs_report.xlsx")Checkpoint: add a Size (KB) column comparing original vs thumbnail file sizes, and a sum formula at the bottom. (Reading Excel back: pd.read_excel — L4.)
L10. Capstone — All the Small Tools, One Robust Script
Goal: combine python-dotenv + tenacity + tqdm + rich + pydantic + requests into one production-shaped program: a resilient API reporter.
"""github_report.py — the utilities tutorial, assembled."""
from dotenv import load_dotenv # 1. config from .env
import os, requests, typer
from tenacity import retry, stop_after_attempt, wait_exponential
from tqdm import tqdm
from rich.console import Console
from rich.table import Table
from pydantic import BaseModel, Field
load_dotenv() # reads .env into os.environ
TOKEN = os.getenv("GITHUB_TOKEN", "")
class Repo(BaseModel): # 2. validated data
name: str
stars: int = Field(alias="stargazers_count")
language: str | None = None
model_config = {"populate_by_name": True}
@retry(stop=stop_after_attempt(4), # 3. retries with backoff
wait=wait_exponential(multiplier=1, min=2, max=30),
reraise=True)
def fetch_repos(user: str, page: int) -> list[dict]:
r = requests.get(f"https://api.github.com/users/{user}/repos",
params={"page": page, "per_page": 30},
headers={"Authorization": f"Bearer {TOKEN}"},
timeout=10)
r.raise_for_status()
return r.json()
def main(user: str):
console = Console()
rows, page = [], 1
with tqdm(desc="fetching", unit="page") as bar: # 4. honest progress
while batch := fetch_repos(user, page):
rows.extend(Repo.model_validate(x) for x in batch)
page += 1; bar.update()
if not rows:
console.print("[yellow]no repos found[/]"); return
table = Table(title=f"{user} — {len(rows)} repositories") # 5. rich output
for col in ["Repo", "Language", "★ Stars"]:
table.add_column(col)
for repo in sorted(rows, key=lambda r: -r.stars)[:15]:
table.add_row(repo.name, repo.language or "—", f"{repo.stars:,}")
console.print(table)
if __name__ == "__main__":
typer.run(main)# .env: GITHUB_TOKEN=ghp_...
python github_report.py torvaldsWhat each piece contributed: dotenv — config without hardcoding secrets · tenacity — survives rate-limits and blips (exponential backoff) · tqdm — progress you can trust · pydantic — the API response is now typed, validated data · rich — output worth screenshotting · typer — --help for free. This is the shape of a real production tool.
Checkpoint — final exercises: (1) add a --language filter option; (2) export the table to CSV (L4); (3) swap fetch_repos to httpx.AsyncClient and gather pages concurrently (L1).
PART 4 — APPENDICES
Appendix A — A–Z Command & Tool Index (Part 1)
| Tools | |
|---|---|
| B | black §8 · build §6 · bandit §8 |
| C | conda/mamba §4 · coverage §7 |
| F | flake8 §8 |
| I | ipython §0.4 · isort §8 · ipdb/pudb §9 |
| J | jupyter lab/notebook/console/nbconvert/kernelspec §10 |
| M | mypy §8 · mamba §4 |
| P | python §1 (all flags) · py launcher §2 · pip §3 · pipx §4 · poetry §4 · pre-commit §8 · pylint §8 · pydoc §5,§0.6 · pdb §9 · pstats §9 |
| R | ruff §8 |
| T | twine §6 · timeit §5,§9 · tracemalloc §9 |
| U | uv §4 (venv/pip/add/sync/run/tool/uvx) · unittest §7 |
| V | venv §4 · virtualenv §4 |
python -m modules (§5): http.server · json.tool · timeit · cProfile · pdb · pydoc · zipfile · tarfile · base64 · compileall · ensurepip · site · sysconfig · unittest · venv · webbrowser · ast · dis · this.
Appendix B — All Built-in Functions
abs(x) | absolute value | hash(o) | identity-based hash |
aiter(x) / anext(x) (3.10+) | async iteration | help([x]) | interactive docs |
all(it) / any(it) | every/any truthy | hex(i) / oct(i) / bin(i) | base strings |
ascii(o) | repr, non-ASCII escaped | id(o) | identity int |
bin(i) | '0b…' | input([prompt]) | read a string |
bool(x) | truthiness | int(x[, base]) | parse/convert |
breakpoint() | drop into pdb | isinstance(o, T) | type test (tuple ok) |
bytearray(x) / bytes(x) | mutable/immutable bytes | issubclass(A, B) | class test |
callable(o) | has __call__ | iter(it) / next(it[,d]) | iterator protocol |
chr(i) / ord(c) | code ↔ character | len(x) | size |
classmethod(f) | classmethod wrapper | list(it) / tuple(it) | sequences |
compile(src,…) | source → code object | locals() / globals() | namespace dicts |
complex(re,im) | complex number | map(f, *its) | lazy transform |
delattr(o,n) / getattr(o,n[,d]) / setattr(o,n,v) / hasattr(o,n) | attribute tools | max(it[,key]) / min(it[,key]) | extremes |
dict(**kw) | new dict | memoryview(b) | zero-copy view |
dir([o]) | name list | object() | base of everything |
divmod(a,b) | (a//b, a%b) | oct(i) | '0o…' |
enumerate(it, s=0) | index+value | open(path,…) | file handle |
eval(e) / exec(e) ⚠ | run dynamic code | pow(b,e[,m]) | power (fast mod) |
filter(f, it) | keep matching | print(*a, sep,end,file,flush) | output |
float(x) | parse float | property(fget,…) | descriptor |
format(v, spec) | __format__ | range(a,b,s) | lazy ints |
frozenset(it) | immutable set | repr(o) | debug string |
reversed(seq) | reverse iterator | round(x[,n]) | banker's rounding |
set(it) | new set | slice(a,b,s) | slice object |
sorted(it,key,rev) | new sorted list | staticmethod(f) | static wrapper |
str(x) | string form | sum(it[,start]) | total |
super() | next-in-MRO proxy | type(o) / type(n,b,d) | type of / build class |
vars([o]) | __dict__ | zip(*its, strict=False) | parallel iteration |
exit() / quit() | leave REPL (never in scripts) | __import__(n) | low-level import |
Appendix C — Dunder Methods Protocol Table
| Protocol | Methods | Triggered by |
|---|---|---|
| Construction | __new__ __init__ __del__ | creation / destruction (GC) |
| String forms | __repr__ __str__ __bytes__ __format__ | repr() print() bytes() f-strings |
| Comparison | __eq__ __ne__ __lt__ __le__ __gt__ __ge__ | == != < <= > >=, sorted |
| Hashing | __hash__ | hash(), dict/set keys (pair with __eq__) |
| Truth | __bool__ __len__ | if x:, bool() |
| Attribute access | __getattr__ __getattribute__ __setattr__ __delattr__ __dir__ | dot access, dir() |
| Descriptors | __get__ __set__ __delete__ __set_name__ | property/class machinery |
| Containers | __len__ __getitem__ __setitem__ __delitem__ __contains__ __missing__ | len() [] in |
| Iteration | __iter__ __next__ __reversed__ __length_hint__ | for, iter(), unpacking |
| Callable | __call__ | obj() |
| Context manager | __enter__ __exit__ | with |
| Arithmetic | __add__ __sub__ __mul__ __truediv__ __floordiv__ __mod__ __pow__ __matmul__ @ | operators |
| Reflected | __radd__ __rsub__ … | right operand implements |
| In-place | __iadd__ __isub__ … | += and friends |
| Unary | __neg__ __pos__ __abs__ __invert__ | -x +x abs(x) ~x |
| Conversions | __int__ __float__ __complex__ __index__ __bool__ | int(), slicing indices |
| Types | __class__ __mro__ __bases__ __slots__ __dict__ | introspection |
| Path/name | __name__ __qualname__ __module__ __file__ | module/function identity |
| Entry point | __main__ | if __name__ == "__main__" |
| Async | __aiter__ __anext__ __aenter__ __aexit__ | async for / async with |
Appendix D — Format Spec Quick Table
| Want | Spec | Example → result |
|---|---|---|
| 2 decimals | .2f | f"{3.14159:.2f}" → 3.14 |
| Thousands | , | f"{1234567:,}" → 1,234,567 |
| Percent | .1% | f"{0.856:.1%}" → 85.6% |
| Hex/oct/bin | x o b (#x prefixed) | f"{255:#x}" → 0xff |
| Padding right/left/center | >10 <10 ^10 | f"{'ab':^6}" → ' ab ' |
| Zero pad | 08d | f"{42:08d}" → 00000042 |
| Sign always | +d | f"{5:+d}" → +5 |
| Exponent | e | f"{12345.6:e}" → 1.234560e+04 |
| Significant digits | .3g | f"{12345.6:.3g}" → 1.23e+04 |
| Debug dump | = | f"{price=}" → price=19.99 |
| repr | !r | f"{'a'!r}" → "'a'" |
| nested width | {v:>{w}} | dynamic width from variable |
| datetime | :%Y-%m-%d | inline date formatting |
Appendix E — The Exceptions You'll Actually Meet
| Exception | Typical cause → fix |
|---|---|
SyntaxError | Typo/unclosed bracket — read the caret line |
IndentationError/TabError | Mixed tabs+spaces — configure editor to spaces |
NameError / UnboundLocal | Typo'd or pre-import use; missing global/nonlocal |
TypeError | Wrong types ("a"+1) or wrong arg count — check signatures |
ValueError | Right type, bad value: int("abc") — validate first |
IndexError / KeyError | Out of range / missing key — .get(), guard lengths |
AttributeError | 'NoneType' has no attribute x — a function returned None (missing return!) |
ImportError/ModuleNotFoundError | Not installed / wrong venv / wrong name — pip install, check sys.path |
FileNotFoundError / PermissionError | Wrong cwd, relative paths — use absolute Paths; run as admin |
ZeroDivisionError | Guard denominators |
StopIteration leaked | next() past end outside a for-loop — give next(it, default) |
RecursionError | Missing base case — rewrite iteratively |
UnicodeDecodeError | Read bytes as utf-8 of a non-utf8 file — pass encoding= / errors= |
TypeError: can't pickle | Lambdas/local objects crossing process boundaries |
OSError errno 22 | Invalid path chars (: in Windows filenames!) |
Full hierarchy tree: §18. Custom errors: subclass Exception, name it SomethingError.
Appendix F — One-Liner Cookbook
# Files & data
python -c "import json;print(json.dumps(json.load(open('x.json')),indent=2))" # pretty JSON
sorted(paths, key=lambda p: p.stat().st_size)[-1] # biggest file
Path("f.txt").read_text().count("ERROR") # occurrences
[*{line.split(',')[0] for line in open('t.csv')}] # unique first column
# Collections
sorted(d.items(), key=lambda kv: -kv[1]) # dict by value desc
[x for x in xs if x not in seen] # dedupe, keep order
list(zip(*matrix)) # transpose
max(words, key=len) # longest word
collections.Counter(nums).most_common(1)[0] # mode
# Strings
", ".join(sorted(set(names))) # unique, sorted, comma'd
s.title() ; s.capitalize() ; " ".join(w.capitalize() for w in s.split())
any(p in text.lower() for p in ["err","fail"]) # keyword alarm
str(int("ff",16)) ; f"{255:08b}" # base conversions
# Clever stdlib
python -m http.server 8000 # share this folder
python -m json.tool messy.json # validate JSON
python -m zipfile -e app.zip out/ # unzip anything
sum(a[1]*b[1] for a,b in zip(prices,qtys)) # dot product
__import__("sys").exit(1) # last-resort inline exitAppendix G — Traceback Decoding, Errors & Best Practices
Reading a traceback (bottom-up):
Traceback (most recent call last): ← read DOWN to find your code…
File "app.py", line 10, in <module> ← your entry: process(invoice)
File "lib.py", line 4, in process
total = sum(row["qty"] for row in rows)
File "lib.py", line 4, in <genexpr>
KeyError: 'qty' ← …the LAST line names the disease- Last line = exception type + message — the actual problem. 2. Walk up the frames to the first file that is yours — that's where to look. 3. The nearest source line shows the crashing expression. Search the exact last line + "python" when stuck.
Best-practice checklist
python -m venv .venvper project — always; commitrequirements.txt/lock file, never the env.python -m pip …inside the env;pipxfor tools.with open(...) as f:and explicitencoding="utf-8"— every time.- f-strings for humans,
logging(not print) for operations,--helpfor every script. - Type-hint public functions; run
ruff check+mypyin CI; test withpytest. - Prefer
pathliboveros.path;subprocess.run(list, check=True, timeout=)overshell=True. - Exceptions: catch narrow,
raiseto re-throw,logging.exceptionto record. - Functions do one thing; dataclasses over bare dicts at API boundaries; comprehensions over map/filter when readable.
- Profile before optimizing (
cProfile,timeit) — 90 % of slowness is I/O or an accidental O(n²). - Pin versions in production (
==/lock), float ranges (>=x,<y) in libraries.
Appendix H — Every Python Library: Complete Standard Library + Essential Third-Party
Scope: the standard library below is the complete set of public, documented modules (Python 3.12/3.13) — organized by purpose. Third-party can never be "complete" (PyPI hosts 600,000+ projects), so Part 2 of this appendix is the curated essential set by domain — the libraries actually worth knowing, chosen by ecosystem share and quality.
H.1 The Standard Library — complete catalog by category
Runtime, interpreter & introspection
| Module | What it does |
|---|---|
sys | Interpreter handles: argv, path, exit, stdout, modules, version |
sysconfig | Build/interpreter configuration & paths |
builtins | The built-in functions/names namespace |
__main__ | Entry-point machinery (if __name__ == "__main__") |
warnings | Warning control (-W, DeprecationWarning) |
atexit | Run functions on interpreter shutdown |
gc | Garbage collector control & stats |
inspect | Live objects: signatures, source, stack frames |
site | Startup path setup (site-packages, .pth) |
stat | File-mode bit constants & interpreters |
contextlib | with helpers: contextmanager, suppress, closing |
abc | Abstract base classes (ABC, abstractmethod) |
dataclasses | Boilerplate-free classes (@dataclass) |
types | Dynamic type creation, SimpleNamespace, NoneType |
copy | Shallow/deep copies |
Text processing
| Module | What it does |
|---|---|
string | Constants, Template, Formatter |
re | Regular expressions |
difflib | Diff/similarity (SequenceMatcher, get_close_matches) |
textwrap | Wrap/fill/dedent paragraphs |
unicodedata | Unicode names, categories, normalization |
stringprep | IDNA string preparation |
readline | Line editing/history (Unix; pyreadline3 on Windows) |
rlcompleter | Tab-completion for the REPL |
codecs | Encoders/decoders registry, open codecs |
Binary data
| Module | What it does |
|---|---|
struct | Pack/unpack C structs to/from bytes |
binascii | hex/base64/uudecode primitives |
Data types & containers
| Module | What it does |
|---|---|
datetime | Dates, times, time zones, arithmetic |
zoneinfo (3.9+) | IANA time-zone database |
calendar | Calendar generation/formatting |
collections | Counter, defaultdict, deque, namedtuple, ChainMap |
collections.abc | Abstract base classes: Iterable, Mapping, … |
heapq | Min-heap priority queue |
bisect | Binary search in sorted lists |
array | Compact typed numeric arrays |
weakref | Garbage-collectable references |
enum | Enumerations (Enum, IntEnum, StrEnum 3.11+) |
graphlib (3.9+) | Topological sorting |
pprint | Pretty-print data structures |
reprlib | Bounded repr for big structures |
Numbers & math
| Module | What it does |
|---|---|
math | Floating math functions & constants |
cmath | Complex-number math |
decimal | Exact decimal arithmetic (money!) |
fractions | Exact rational numbers |
random | Pseudo-random numbers (not crypto) |
statistics | Mean, median, stdev, quantiles, correlation |
numbers | Numeric tower ABCs (Integral, Real) |
Functional programming
| Module | What it does |
|---|---|
itertools | Iterator algebra: product, chain, groupby… |
functools | cache, partial, reduce, wraps |
operator | Function forms of operators (itemgetter, attrgetter) |
Concurrency & parallelism
| Module | What it does |
|---|---|
asyncio | The async event-loop framework |
threading | Threads, locks, events |
multiprocessing | Processes, pools, queues (true parallelism) |
multiprocessing.shared_memory | Zero-cross-process shared buffers |
concurrent.futures | ThreadPoolExecutor/ProcessPoolExecutor |
subprocess | Run external programs |
queue | Thread-safe queues (Queue, LifoQueue, PriorityQueue) |
sched | Delayed event scheduler |
contextvars | Context-local state (async-safe “globals”) |
signal | POSIX signal handlers |
select | Wait on streams (select/epoll/kqueue) |
selectors | High-level select abstraction |
File system & I/O
| Module | What it does |
|---|---|
pathlib | Object-oriented paths (§20) |
os | OS interface: environ, process, walk |
os.path | Legacy path utilities |
io | Streams: StringIO, BytesIO, buffers |
shutil | High-level file ops: copytree, rmtree, which |
tempfile | Temporary files/directories |
glob | Wildcard path matching |
fnmatch | Unix-style pattern matching on names |
fileinput | Iterate over lines of many files/stdin |
linecache | Random access to file lines (tracebacks use it) |
filecmp | File/directory comparison |
getpass | Password prompts without echo |
Persistence & databases
| Module | What it does |
|---|---|
pickle | Object serialization (⚠ untrusted data) |
copyreg | Register pickle support for types |
shelve | Persistent dict of pickled objects |
marshal | Fast internal serialization (bytecode) |
dbm | Simple key-value DBs (dbm.sqlite 3.13) |
sqlite3 | Embedded SQL database (§20) |
plistlib | Apple property lists |
Compression & archives
| Module | What it does |
|---|---|
zlib | Raw DEFLATE compression |
gzip | gzip files/streams |
bz2 / lzma | bzip2 / xz (LZMA) |
zipfile | Zip archives (also creator of .whl) |
tarfile | tar archives |
Structured data & config formats
| Module | What it does |
|---|---|
json | JSON encode/decode |
csv | CSV/TSV reading & writing |
configparser | INI files |
tomllib (3.11+) | Parse TOML (pyproject files) |
netrc | .netrc login files |
email | MIME message building/parsing (package) |
mailbox | mbox/MH mail folder access |
mimetypes | Filename ↔ MIME type mapping |
Markup & internet data
| Module | What it does |
|---|---|
html / html.parser / html.entities | HTML escape/parse |
xml.etree.ElementTree | The sane XML API |
xml.dom / xml.dom.minidom / xml.dom.pulldom | DOM APIs |
xml.sax | SAX (streaming) parsing |
xml.parsers.expat | Expat parser bindings |
base64 | Base16/32/64 encoding |
quopri | Quoted-printable encoding |
urllib.parse | URL splitting/joining/encoding |
Networking
| Module | What it does |
|---|---|
socket | BSD sockets (TCP/UDP/Unix) |
ssl | TLS/SSL wrapping |
socketserver | TCP/UDP server skeletons |
ipaddress | IPv4/IPv6 address & network math |
uuid | UUID generation (1/3/4/5) |
mmap | Memory-mapped files |
Internet protocols (clients & servers)
| Module | What it does |
|---|---|
urllib.request / urllib.error / urllib.robotparser | URL fetching (stdlib HTTP) |
http.client | Low-level HTTP client |
http.server | The quick dev web server |
http.cookies / http.cookiejar | Cookie handling |
ftplib | FTP client |
imaplib | IMAP mail client |
poplib | POP3 mail client |
smtplib | SMTP sending |
nntplib † | Usenet — removed in 3.13 |
telnetlib † | Telnet — removed in 3.13 |
xmlrpc.client / xmlrpc.server | XML-RPC |
webbrowser | Open the user's browser |
wsgiref | WSGI server/utilities |
Crypto & hashing
| Module | What it does |
|---|---|
hashlib | SHA-2/3, blake2, MD5 digests |
hmac | Keyed message authentication |
secrets | Crypto-safe tokens/random |
OS, platform & environment
| Module | What it does |
|---|---|
time | Clock reading, sleeping, formatting |
argparse / getopt | CLI parsing (§22) |
logging (+ .config, .handlers) | Logging framework (§22) |
gettext / locale | Internationalization |
platform | OS/machine/Python detection |
errno | System error-code constants |
ctypes | Call C shared libraries |
curses | Terminal UIs (Unix; windows-curses on Win) |
msvcrt / winreg | Windows: MSVC runtime, registry |
pwd / grp / termios / tty / pty / resource / syslog / posix | Unix-only system interfaces |
Multimedia
| Module | What it does |
|---|---|
wave | WAV file read/write |
colorsys | RGB ↔ HLS/HSV conversion |
(removed 3.13) audioop, aifc, sunau, chunk, imghdr, sndhdr | Legacy audio/image helpers — now third-party |
GUI & simple frameworks
| Module | What it does |
|---|---|
tkinter (+ ttk, scrolledtext) | Standard GUI toolkit (Tcl/Tk) |
turtle | Turtle graphics (learning) |
cmd | Line-oriented command interpreters |
shlex | Shell-like lexing/quoting |
Development, testing & debugging
| Module | What it does |
|---|---|
typing | Type hints: Optional, Literal, Protocol… |
doctest | Tests embedded in docstrings |
unittest (+ unittest.mock) | xUnit test framework (§7) |
pydoc | Doc extraction (python -m pydoc) |
pdb / bdb | Debugger engine & builder base (§9) |
faulthandler | Tracebacks on hard crashes |
timeit | Micro-benchmarks |
trace | Execution tracing |
tracemalloc | Memory-allocation tracing |
code / codeop | Embedded interactive interpreters |
Import system & language services
| Module | What it does |
|---|---|
importlib (+ .resources, .metadata) | Programmatic imports, data files, dist info |
zipimport | Import from zip archives |
modulefinder | Find modules a script imports |
ast | Parse/transform Python source trees |
symtable | Symbol tables from the compiler |
token / keyword / tokenize | Language tokens & keywords |
tabnanny | Ambiguous-indentation checker |
pyclbr | Class/function browser info |
py_compile / compileall | Bytecode compilation |
dis | Disassembler |
Removed-in-3.13 watchlist (you'll still meet these in old code): aifc audioop cgi cgitb chunk crypt imghdr mailcap msilib nis nntplib ossaudiodev pipes sndhdr spwd sunau telnetlib uu xdrlib lib2to3 — plus formatter/parser (gone in 3.10) and asyncore/asynchat (gone in 3.12).
H.2 Essential Third-Party Libraries by Domain
Web frameworks & APIs
| Library | What it does |
|---|---|
django | The batteries-included web framework (ORM, admin, auth) |
flask | Micro-framework — you choose the pieces |
fastapi | Modern async APIs with automatic OpenAPI docs |
starlette | The ASGI toolkit under FastAPI |
litestar | Fast, typed, async framework (FastAPI rival) |
sanic / tornado | High-performance async frameworks |
bottle / pyramid | Minimal / mature mid-size options |
jinja2 | The template engine (used by Flask & Ansible) |
uvicorn / gunicorn / hypercorn | ASGI / WSGI production servers |
strawberry-graphql / graphene | GraphQL APIs |
HTTP & network clients
| Library | What it does |
|---|---|
requests | The human HTTP client — de-facto standard |
httpx | requests-compatible + async + HTTP/2 |
aiohttp | Async HTTP client & server |
urllib3 | Power underneath requests |
websockets / websocket-client | Async / sync WebSocket |
pysocks / python-socks | SOCKS proxies |
Web scraping & parsing
| Library | What it does |
|---|---|
beautifulsoup4 | Tolerant HTML parsing & extraction |
lxml | Fast C XML/HTML (XPath, XSLT) |
scrapy | Full crawling/spider framework |
selectolax / parsel | Very fast HTML parsers / XPath+CSS selectors |
Databases & ORMs
| Library | What it does |
|---|---|
sqlalchemy | The SQL toolkit & ORM — industry standard |
peewee / pony / tortoise-orm | Small sync ORMs / async ORM |
alembic | Schema migrations for SQLAlchemy |
psycopg (v3) / psycopg2-binary | PostgreSQL drivers |
pymysql / mysql-connector-python / mariadb | MySQL/MariaDB drivers |
pymongo / motor | MongoDB sync / async |
redis / hiredis | Redis client + C parser |
elasticsearch | Elasticsearch client |
influxdb-client | Time-series DB client |
cassandra-driver / neo4j | Wide-column / graph DB drivers |
Data analysis & dataframes
| Library | What it does |
|---|---|
numpy | N-dim arrays — foundation of all science in Python |
pandas | DataFrames: wrangle, join, aggregate tabular data |
polars | Rust-powered DataFrames, much faster than pandas |
pyarrow | Columnar Arrow memory/format (parquet, interop) |
dask / modin | Parallel/out-of-core pandas scaling |
great-expectations / pandera | Data validation |
Visualization
| Library | What it does |
|---|---|
matplotlib | The plotting foundation |
seaborn | Statistical charts on matplotlib |
plotly | Interactive charts & dashboards |
bokeh | Interactive browser visualizations |
altair | Declarative statistical graphics |
folium | Leaflet maps |
Machine learning & statistics
| Library | What it does |
|---|---|
scikit-learn | Classical ML: regression, trees, clustering, pipelines |
xgboost / lightgbm / catboost | Gradient-boosting champions |
statsmodels | Classical statistics & econometrics |
optuna | Hyperparameter optimization |
imbalanced-learn | Resampling for skewed classes |
Deep learning & AI
| Library | What it does |
|---|---|
torch (PyTorch) | Research-favorite deep-learning framework |
tensorflow / keras | Production DL stack |
jax / flax | Composable autodiff on GPU/TPU |
transformers (HF) | Pretrained models — NLP and beyond |
diffusers | Stable-diffusion image generation |
ultralytics | YOLO object detection |
openai / anthropic / google-genai | LLM provider SDKs |
langchain / llama-index | LLM application frameworks |
sentence-transformers | Embeddings & semantic search |
whisper (openai-whisper) | Speech-to-text |
onnx / onnxruntime | Model interchange & inference |
mlflow / wandb / ray | Experiment tracking / distributed compute |
Computer vision & images
| Library | What it does |
|---|---|
opencv-python | The CV swiss army knife |
pillow | Image read/write/resize (PIL fork) |
scikit-image | CV algorithms on numpy arrays |
imageio / imgaug / albumentations | IO for all formats / training augmentations |
easyocr / paddleocr | OCR engines |
cairosvg / wand | SVG→PNG / ImageMagick bindings |
Audio, video & multimedia
| Library | What it does |
|---|---|
pydub | Easy audio cutting/conversion |
librosa | Music & audio analysis |
soundfile / pyaudio | Read-write sound / record & play |
speechrecognition | STT front-ends |
moviepy / ffmpeg-python | Video editing / ffmpeg bindings |
GUI & desktop apps
| Library | What it does |
|---|---|
pyside6 / pyqt6 | Qt applications (official / commercial-ish) |
kivy | Natural UIs, touch, mobile |
wxpython | Native-widget toolkit |
customtkinter | Modern-looking tkinter |
dearpygui | GPU-rendered immediate-mode GUI |
flet / nicegui | Flutter-style / web-tech UIs in Python |
pystray / pywin32 | Tray icons / full Windows API |
Games
| Library | What it does |
|---|---|
pygame / pygame-ce | The classic 2D game engine (CE = community fork) |
arcade | Modern 2D, OpenGL |
panda3d / ursina | 3D engine / cute 3D wrapper |
renpy | Visual novels |
CLI & terminal UX
| Library | What it does |
|---|---|
typer / click | Type-hint / decorator CLI builders |
rich | Rich text, tables, progress in terminal |
textual | Full TUI apps (by the rich author) |
prompt-toolkit / questionary / inquirer | Interactive prompts |
tqdm / alive-progress | Progress bars for loops |
colorama / blessed | Cross-platform colors / terminal control |
Testing & quality
| Library | What it does |
|---|---|
pytest (+ pytest-cov, pytest-xdist, pytest-mock, pytest-asyncio) | Testing framework & plugins |
hypothesis | Property-based testing |
tox / nox | Matrix test automation |
faker / factory-boy | Fake data / object factories |
selenium / playwright | Browser automation & E2E |
locust | Load testing |
robotframework | Keyword-driven acceptance testing |
coverage | Line/branch coverage |
Linting, formatting & typing
| Library | What it does |
|---|---|
ruff ⭐ | Linter+formatter, Rust-fast (replaces many below) |
black / isort | Formatter / import sorter |
mypy / pyright | Static type checkers |
pylint / flake8 / pydocstyle | Classic linters / docstring style |
bandit / semgrep | Security scanners |
pre-commit | Git hook manager |
pylint-django etc. | Framework-aware plugins |
Packaging, build & environments
| Library | What it does |
|---|---|
pip / setuptools / wheel | The install/build base |
build / twine | Build sdist+wheel / upload to PyPI |
poetry / pdm / hatch | Project managers |
uv ⭐ | Blazing-fast everything (pip/venv/pipx/python) |
pipx | Install CLI apps isolated |
pipenv / virtualenv | Legacy env managers |
pyinstaller / nuitka / cx-freeze | Freeze to standalone executables |
shiv / pex | Zipapps — single-file executables |
conda / mamba | Science-oriented env/package managers |
Task queues, scheduling & workflow
| Library | What it does |
|---|---|
celery | Distributed task queue standard |
rq / dramatiq / huey | Lighter task queues |
apscheduler | In-process job scheduling |
airflow / prefect / dagster | Data-pipeline orchestrators |
kafka-python / confluent-kafka | Kafka clients |
pika | RabbitMQ (AMQP) |
pyzmq | ZeroMQ messaging |
nats-py | NATS messaging |
Serialization, validation & settings
| Library | What it does |
|---|---|
pydantic (v2) | Data validation via type hints (FastAPI's core) |
attrs / cattrs | Class builder without inheritance |
marshmallow | Schema serialization |
orjson / ujson | Ultra-fast JSON |
msgspec ⭐ | Fast JSON/msgpack + validation |
protobuf / flatbuffers / thrift | Binary schema formats |
python-dotenv / pydantic-settings / dynaconf | Env/config loading |
hydra-core / omegaconf | Hierarchical experiment configs |
Office documents & PDFs
| Library | What it does |
|---|---|
openpyxl / xlsxwriter | Excel xlsx read-write / fast writing |
python-docx / python-pptx | Word / PowerPoint editing |
pypdf / pdfplumber | PDF merge/split / text+table extraction |
reportlab / fpdf2 / weasyprint | PDF generation (programmatic / HTML→PDF) |
camelot / tabula-py | Table extraction from PDFs |
python-docx-template / docxtpl | Jinja2 templates for Word |
pandoc (pypandoc) | Universal document conversion |
Date, time & utilities
| Library | What it does |
|---|---|
python-dateutil | Parser (parse("3 days ago"…)), rrule |
pendulum / arrow | Nicer datetime libraries |
pytz | Legacy tz database (use zoneinfo now) |
freezegun / time-machine | Freeze time in tests |
humanize / prettytable / tabulate | Human strings / ASCII tables |
more-itertools / toolz | Even more iterator/functional helpers |
cytoolz | C-accelerated toolz |
System, environment & observability
| Library | What it does |
|---|---|
psutil | Processes, CPU, memory, disks, network |
watchdog | Cross-platform filesystem events |
pyperclip | Clipboard access |
send2trash | Safe delete-to-recycle-bin |
diskcache / cachetools / aiocache | Persistent / in-proc / async caches |
loguru / structlog | Painless / structured logging |
sentry-sdk / opentelemetry | Crash reporting / tracing |
Security & crypto
| Library | What it does |
|---|---|
cryptography | The modern crypto library (TLS, X.509, AEAD) |
pyjwt / python-jose | JWT encode/verify |
passlib / argon2-cffi / bcrypt | Password hashing |
authlib | OAuth1/2, OIDC |
paramiko / fabric / netmiko | SSH / SSH automation / network devices |
scapy | Packet crafting & sniffing |
impacket / pwntools | Windows protocols CTF/pentest toolkit |
certifi | The CA certificate bundle trusted by requests/httpx |
Science & engineering
| Library | What it does |
|---|---|
scipy | Optimization, signal, linear algebra, stats |
sympy | Symbolic math (CAS) |
numba / cython | JIT / compile Python to C speed |
networkx | Graph algorithms |
pint / uncertainties | Units / error propagation |
astropy | Astronomy |
biopython | Bioinformatics |
qutip | Quantum physics |
geopandas / shapely / rasterio | Geospatial dataframes / geometry / rasters |
Docs, notebooks & education
| Library | What it does |
|---|---|
jupyterlab / notebook / ipython | Interactive computing |
ipywidgets / papermill | Widget UIs / parameterized notebooks |
sphinx / mkdocs-material | Doc site generators |
mkdocstrings / autodoc | API docs from docstrings |
pdoc | Zero-config API docs |
H.3 How to choose a library (30-second method)
pip index versions name— is it alive? Last release recent?- Check PyPI page: does it support your Python version? Wheel available (no compiler needed)?
- Gut-check health: maintained repo, issues answered, tests, license permissive (MIT/BSD/Apache)?
- Any heavyweight deps it drags in? (
pip install --dry-run) - Search “alternatives to X” — the ecosystem moves (requests→httpx, pandas→polars, pip→uv).
- For anything security-sensitive: is it widely audited (
cryptography, not random forks)?
Every package above is installable with pip install name / uv pip install name (§3–§4) — and browsable at pypi.org/project/<name> with docs, changelog and dependency info.
Keep Learning
- Official tutorial & library reference: docs.python.org/3 (the single best free resource)
- Practice: exercism.org/tracks/python, adventofcode.com, project ideas → build, don't just read
- Real-world code reading: browse the stdlib source (
Lib/in the CPython repo) — it's readable Python! - 7-day path with this doc: Day 1 §0–1 · Day 2 §11–14 · Day 3 §15–17 · Day 4 §18–19 · Day 5 §20 · Day 6 §21–22 · Day 7 any of T/L tutorials — then L1→L10 across week two
- When stuck: read the traceback bottom-up →
help()/dir()→ docs.python.org → search the exact error line
End of reference — 70 built-ins, 40+ tool subcommand tables, complete language core, 20 tutorials (T1–T10 + L1–L10), complete library catalog. Happy hissing. 🐍