Python
A readable general-purpose language used for scripting, data work, web back ends and teaching.
Beginner
Read and write small programs with confidence.
Getting started
- Installing Python20 minInstall Python 3.15, confirm it runs from a terminal, and choose between the python.org installer and uv.
- The REPL and running scripts20 min · Python 3.13+Try Python interactively in the REPL, then save your work in a .py file and run it from the terminal.
- print, input and your first program25 minWrite a program that asks a question, converts the answer to a number and prints a reply.
- Comments, indentation and style20 minWrite readable Python with correct indentation, helpful comments and the naming conventions of PEP 8.
- Python values and types25 minName Python's basic types, check them with type and isinstance, and convert between them on purpose.
- Variables and names25 minUnderstand that a Python variable is a name bound to an object, and use assignment, swapping and constants correctly.
Numbers and text
- Working with numbers30 minUse Python's int and float types, the arithmetic operators, abs, min, max and round, and Decimal and Fraction when you need exact results.
- Operators and precedence25 minPredict the result of Python expressions using precedence, chained comparisons, and the way and and or return operands.
- Strings25 minCreate strings, use escapes and raw strings, and read parts of a string with indexing and slicing.
- f-strings and formatting30 min · Python 3.12+Build formatted text with f-strings, using format specs for alignment, padding, separators, percentages and debugging output.
- String methods25 minClean, split, join, search and transform text with the most useful str methods.
- Unicode, bytes and encodings30 minConvert between str and bytes safely, and read and write text files with an explicit encoding.
Control flow
- Booleans and truthiness25 minPredict whether any Python object counts as true or false, and choose between ==, is and is None.
- if, elif, else30 minWrite branching logic with if, elif and else, pick values with the conditional expression, and keep nesting flat by checking bad cases first.
- while loops30 minRepeat code while a condition holds, read input until a sentinel appears, find bugs in a loop with print debugging, and stop loops that would never end.
- for loops and range30 minIterate over sequences with for, count with range, count backwards with reversed, number items with enumerate, and pair sequences with zip.
- Loop control and loop else25 minSteer loops with break and continue, understand how nested loops behave, and use the else clause on a loop to detect a search that found nothing.
- match statements (intro)30 minReplace long if/elif chains with match, using literal, capture, wildcard and alternative patterns plus guards.
Collections
- Lists35 minCreate, change and sort Python lists, and avoid the aliasing bug that catches almost every beginner.
- Tuples and unpacking30 minGroup values in immutable tuples, pull them apart by unpacking, and collect leftovers with star syntax.
- Dictionaries40 minMap keys to values with dicts, look things up safely, merge dicts with the pipe operator, and preview the new immutable frozendict.
- Sets30 minUse sets for fast membership tests, removing duplicates and set algebra with union, intersection and difference.
- Slicing in depth30 minCut lists, strings and tuples with start, stop and step, use negative indexes, and change lists with slice assignment.
- Comprehensions40 min · Python 3.15+Build lists, dicts and sets from loops in one expression, filter as you go, and flatten nested data with the Python 3.15 unpacking form.
- Choosing the right collection35 minPick between list, tuple, dict, set and deque by the operations you need, and measure the difference with a stopwatch.
Functions
- Defining functions30 minWrite Python functions with def, return values from them, and document them with docstrings.
- Parameters in depth40 minUse default values, *args, **kwargs, keyword-only and positional-only parameters, and avoid the mutable-default trap.
- Scope and the LEGB rule35 minPredict which variable a name refers to in Python, use global and nonlocal correctly, and stop shadowing built-ins.
- Lambdas and higher-order functions35 minPass functions as values, write small lambda expressions, and sort by several keys with key=.
- Recursion in Python35 minWrite recursive functions in Python, and know the recursion limit, RecursionError and when to use a loop instead.
- Type hints (intro)30 minAnnotate function parameters and return values, and use a type checker to catch mistakes Python itself will not.
Putting it together
- Reading tracebacks and error messages40 minRead a Python traceback from the bottom up, tell the common exception types apart, and use the message and caret markers to find the real problem.
- Handling errors with try and except40 minCatch the errors you expect with try, except, else and finally, ask again after bad input, and leave unexpected bugs visible.
- Modules and the import statement35 minBring in code from Python's standard library with import and from, explore a module with dir and help, and use math, random, statistics and datetime.
- Reading and writing text files40 minOpen a text file with with open and encoding utf-8, read it whole or line by line, write and append, and handle a missing file.
- Debugging habits35 minFind logic bugs with print debugging, f"{x=}" and !r, and pause a running program with breakpoint() and the pdb debugger.
- Mini-project: a word-frequency counter60 minBuild a complete program in five stages that reads a text file, counts its words, ranks them and saves a report, using everything from the beginner level.
Intermediate
Structure larger programs and use the standard library well.
Errors and files
- The exception family tree35 minUse the exception hierarchy to catch whole families of errors, order handlers correctly, and choose between look-first and ask-forgiveness code.
- Raising and chaining exceptions40 minRaise exceptions with clear messages, re-raise them after logging, and chain one error to another with raise from so the cause is never lost.
- Notes and exception groups40 minAttach context to an exception with add_note, and raise and handle several unrelated errors at once with ExceptionGroup and except*.
- Paths and folders with pathlib40 minBuild file paths that work on every operating system, read and write whole files in one call, and list or search folders with pathlib.Path.
- Text, bytes and newlines40 minRead and write files as raw bytes, process large files in chunks, recover from decoding errors, and control newline handling.
- The with statement in depth40 minUnderstand what with guarantees, combine several context managers, and use contextlib and tempfile to clean up after yourself automatically.
- CSV, JSON and TOML45 minRead and write tables with csv, exchange nested data with json, and load configuration files with tomllib, all from the standard library.
- Dates and times50 minWork with date, datetime and timedelta, format and parse timestamps, and keep time zones straight with aware datetimes and zoneinfo.
Modules, packages and tooling
- Writing your own modules40 minSplit a program across files, keep import-time side effects out of your modules with the __name__ guard, and read sys.path to see where Python looks.
- Packages40 minGroup modules into a package folder, wire it together with __init__.py and relative imports, and run package code the way Python expects.
- Virtual environments and pip40 minCreate an isolated environment for each project, install third-party packages into it with pip, and record what you installed so someone else can repeat it.
- pyproject.toml and uv45 minDescribe a Python project in one pyproject.toml file, install it into an environment, and manage environments, dependencies and lock files with uv.
- Single-file scripts with dependencies25 minDeclare a script's Python version and dependencies in a comment block at the top of the file, and run it with uv without creating a project or environment yourself.
- Linting, formatting and type checking35 minUse ruff to catch mistakes and normalise formatting, configure it in pyproject.toml, and run a type checker over annotated code.
- Lazy imports30 min · Python 3.15+Defer the cost of importing a module until the first time you use it, using the lazy keyword from PEP 810, and measure the effect with -X importtime.
- A tour of the standard library40 minLearn where the standard library already solves common jobs, from the operating system to statistics, and how to find the right module in the documentation.
Object-oriented Python
- Classes and objects45 minDefine your own types with class, give each instance its own state through __init__ and self, and tell instance attributes from class attributes.
- Properties, class methods and static methods50 minHide internals by convention, expose computed and validated attributes with @property, and choose between instance, class and static methods.
- Inheritance and composition55 minReuse code with subclasses and super(), mark intentional overrides with @override, and know when to hold another object instead of inheriting from it.
- Defining your own exceptions40 minWrite exception classes that form a family, carry useful data, and chain from the low-level errors they replace.
- The Python data model60 minMake your own classes work with print, ==, sorting, len, for loops and operators by implementing the special methods Python calls behind the scenes.
- Writing a context manager class45 minBuild your own with-statement support using __enter__ and __exit__, read the exception details in __exit__, and decide whether to swallow an error.
- Dataclasses55 minGenerate __init__, __repr__ and __eq__ from a list of annotated fields, then add frozen, slots and keyword-only options as needed.
- Enums45 minModel a fixed set of choices with Enum, IntEnum and StrEnum, look members up by name and value, and branch on them with match.
- Abstract base classes and protocols50 minDefine what an interface looks like in Python, using duck typing, abstract base classes that refuse incomplete subclasses, and Protocols that checkers verify without inheritance.
Iteration and functional tools
- The iterator protocol35 minExplain how a for loop really works, and write your own iterable and iterator classes with __iter__ and __next__.
- Generators40 minWrite generator functions and generator expressions that produce values one at a time, and chain them into lazy pipelines.
- Writing context managers with generators30 minTurn a generator function into a context manager with contextlib.contextmanager, and make sure the clean-up runs even when the block fails.
- itertools and functools45 minReach for the standard library's iterator and function tools, from chain and groupby to partial, cache and singledispatch.
- Closures and higher-order functions40 minBuild functions that remember their surroundings, change captured state with nonlocal, and avoid the late-binding bug in loops.
- Decorators45 minWrite decorators that add behaviour around a function, keep its identity with functools.wraps, and take their own arguments.
- Sorting and key functions35 minSort records by one or several fields, in mixed directions, and rely on stability to get tie-breakers right.
Testing, debugging and quality
- Automated tests with unittest40 minWrite test cases with the standard library's unittest, run them from the command line, and read what a failing test tells you.
- Testing with pytest40 minWrite tests as plain functions with assert, run them with pytest, read its failure reports, and check exceptions with pytest.raises.
- Fixtures, parametrization and temporary files45 minShare set-up between pytest tests with fixtures, run one test over many inputs, and isolate files and environment variables with tmp_path and monkeypatch.
- A test-driven workflow and a quality gate45 minGrow a function one failing test at a time, measure what your tests miss with coverage, and collect the checks into one command that runs before every commit and in CI.
- Mocking with unittest.mock45 minReplace slow, random or external parts of a program with test doubles using Mock, patch and autospec, and check how your code called them.
- Docstrings and doctest40 minDocument functions with docstrings that help() and editors can show, and put runnable examples in them so the documentation is tested.
- Logging instead of print45 minReplace print statements with the logging module, choose levels, send records to the console and a file, and keep secrets out of the log.
- Debugging with pdb45 minGo beyond breakpoint() with conditional breakpoints, post-mortem debugging of crashes, scripted sessions, and attaching pdb to a running process.
- Measuring speed with timeit and cProfile40 minTime small snippets with timeit, find the slow function with cProfile, and learn where the 3.15 sampling profiler fits.
Advanced
Typing in depth, advanced data handling, concurrency and metaprogramming.
Typing in depth
- Static typing fundamentals55 minType real code precisely with Callable, Literal, Final and TypedDict, know when Any switches checking off, and use cast and reveal_type deliberately.
- Generics with type parameter syntax50 min · Python 3.12+Write generic functions, classes and type aliases with the square-bracket syntax from Python 3.12, add bounds, constraints and defaults, and see what the type parameters look like at run time.
- Protocols and structural typing50 min · Python 3.12+Type duck-typed code with protocols that describe methods, attributes and call signatures, and write decorators that keep the signature of the function they wrap, with ParamSpec and Concatenate.
- Type narrowing45 min · Python 3.13+See how checkers narrow unions through is None, isinstance and equality, write your own narrowing functions with TypeIs and TypeGuard, and make match statements provably exhaustive with assert_never.
- Annotations at runtime50 min · Python 3.14+Understand how Python 3.14 stores annotations lazily, read them safely with annotationlib in three formats, resolve them with typing.get_type_hints, and use Annotated metadata to drive a small validator.
- TypedDict in depth45 minModel JSON-like data precisely with optional and required keys, read-only keys, typed keyword arguments, and the closed and extra_items options added in Python 3.15.
- New in typing (3.15)40 min · Python 3.15+Use the typing features that arrived in Python 3.15, the sentinel builtin, TypeForm and disjoint bases, alongside the slightly older Self and @override that modern code relies on.
Advanced data handling
- Structural pattern matching in depth45 minDestructure dictionaries, nested sequences and your own classes with match, and avoid the patterns that silently match everything.
- The collections module55 minMultiset arithmetic with Counter, grouping with defaultdict, an LRU cache with OrderedDict, records with namedtuple, scopes with ChainMap, and your own mappings with UserDict and the collections.abc classes.
- Immutability and frozendict40 minDesign data that cannot change under you, with tuples, frozensets, frozen dataclasses, read-only views and the frozendict built-in.
- Copying and references40 minFind and fix aliasing bugs, choose between shallow and deep copies, and control how your own classes are copied and replaced.
- Regular expressions55 minWrite regular expressions you can maintain and trust, with named groups, lookaround, verbose patterns, tokenizers, possessive quantifiers and patterns that cannot backtrack catastrophically.
- Algorithms the Pythonic way50 minBinary search with bisect, priority queues with heapq, and graph searches built from dictionaries and deques.
Concurrency and parallelism
- Concurrency models in Python35 min · Python 3.14+Tell I/O-bound work from CPU-bound work, see what the GIL does and does not stop, and choose between threads, processes, asyncio, interpreters and the free-threaded build.
- Threads45 minStart and join threads, see a race condition happen on purpose, fix it with locks, coordinate threads with queues, events, semaphores and conditions, and run I/O-bound jobs with ThreadPoolExecutor.
- Processes40 minRun CPU-bound Python on every core with ProcessPoolExecutor, and learn the rules that come with separate processes - the main guard, pickling, copied state and start methods.
- asyncio fundamentals45 minWrite coroutines with async def and await, overlap waits with tasks and gather, consume streams with async for and async generators, push blocking calls to a thread with asyncio.to_thread, and run it all with asyncio.run and asyncio.Runner.
- Structured concurrency45 minRun many asyncio tasks safely with TaskGroup, feed workers through an asyncio.Queue, put deadlines on them with asyncio.timeout, handle cancellation and grouped errors correctly, and inspect a running program with python -m asyncio ps.
- Free-threaded Python40 min · Python 3.14+Install and detect the free-threaded build, run CPU-bound threads in parallel, and learn what changes for thread safety and extension modules when the GIL is gone.
- Multiple interpreters40 min · Python 3.14+Create isolated interpreters in one process with concurrent.interpreters, move data between them, and run CPU-bound work in parallel with InterpreterPoolExecutor.
Metaprogramming
- Descriptors55 minSee how Python looks up an attribute, write descriptors with __get__, __set__ and __set_name__, recognise properties and methods as descriptors in disguise, and hook the end of lookup with __getattr__.
- Customising classes45 minHook into class creation with class decorators, __init_subclass__ and __set_name__, make a class subscriptable with __class_getitem__, and fix an instance's attributes with __slots__.
- Metaclasses50 minSee that classes are instances of type, write metaclasses with __new__, __init__, __prepare__ and __call__, read annotations safely during class creation, and learn the simpler tools that usually replace a metaclass.
- Advanced context managers45 minBuild reusable context managers with ExitStack callbacks and pop_all, change directory safely with contextlib.chdir, use managers as decorators, and write async context managers.
- Template strings40 min · Python 3.14+Use t-string literals to get the parts of an interpolated string before they are combined, and process them safely for HTML, SQL and logging.
- Introspection and the AST50 minInspect functions at run time with the inspect module, parse source code into an abstract syntax tree, analyse and transform it with visitors, take a first look at bytecode, and avoid the dangers of eval and exec.
Expert
Performance, CPython internals and production practice, ending with a project that ships a fast tool.
Performance
- Measuring performance40 minChoose the right clock, build a benchmark you can trust, report results that survive noise, and tell growth rate from constant factors.
- Profiling45 min · Python 3.15+Choose between tracing and sampling, find who calls a hot function, read flame graphs, and profile a live process with Tachyon, the 3.15 sampling profiler.
- Memory45 minMeasure memory honestly with sys.getsizeof and tracemalloc, find the line that allocates, and cut usage with __slots__, streaming and the 3.15 bytearray.take_bytes.
- Optimising Python code50 minApply optimisations in the order that pays, from algorithms and data structures to built-ins, caching, vectorised libraries and parallelism, and check each one with a measurement.
- Native extensions45 minCall C from Python with ctypes, compare cffi, Cython and Rust with PyO3, and understand the stable ABI, including abi3t for free-threaded builds in 3.15.
- The JIT and the interpreter40 minHow CPython executes your code, from the specialising adaptive interpreter (tier 1) and its tail-calling build to the experimental JIT (tier 2), and what each means for how you write fast Python.
CPython internals
- Bytecode and dis45 min · Python 3.12+Read CPython bytecode with dis, take code objects apart, map instructions back to source lines and columns with co_lines() and positions, and watch the specialising interpreter rewrite hot code.
- Memory management50 minHow CPython frees objects with reference counting, why some objects are immortal, how the cyclic garbage collector finds what reference counting misses, what happened to the 3.14 incremental collector, and how weak references break cycles.
- The GIL and free-threading internals45 min · Python 3.14+What the global interpreter lock actually protects, why removing it needed new reference counting, per-object locks and a new collector design, how extensions declare they are ready, and where PEP 779 leaves the free-threaded build.
- The import system45 minFollow an import statement through sys.modules, meta path finders, specs and loaders, write finders and loaders of your own, see how lazy imports defer the search, and replace .pth import lines with .start files.
- Monitoring and remote debugging45 min · Python 3.14+Instrument running code with sys.monitoring, build a call counter and a coverage tool that switch themselves off, attach pdb to a live process, run code in another process with sys.remote_exec, and see why 3.15 builds with frame pointers.
- A tour of the C API50 min · Python 3.15+Read and write a small CPython extension in C: PyObject pointers, owned and borrowed references, error returns, argument parsing, a method table and the new PyModExport export hook of Python 3.15. Then build it for the stable ABIs abi3 and abi3t.
Production Python
- Building and publishing packages50 minTurn a pyproject.toml project into a source distribution and a wheel, read what is inside them, choose version numbers, publish through TestPyPI and trusted publishing, and pin whole environments with the standard pylock.toml lock file.
- Command-line applications45 minBuild a command-line tool with argparse subcommands, keep results on stdout and problems on stderr, return meaningful exit codes, run it with python -m or an installed command, and test it in-process.
- Secure Python45 minThe Python-specific ways programs get compromised and their fixes: secrets instead of random, password hashing, subprocess without a shell, why pickle runs code, path traversal, TLS verification, untrusted XML and archives, and auditing dependencies.
- Configuration and observability55 minRun a Python service in production: typed configuration read once from the environment, logging set up with dictConfig, structured JSON logs with request IDs from a context variable, a tracing sketch, health checks, a clean stop on SIGTERM, and faulthandler for crashes and hangs.
- Architecture for large codebases50 minKeep a growing Python application changeable: layers with one-way dependencies, dependency injection through constructors and Protocols, a composition root, boundaries enforced by a checker, typing as documentation, shared state that survives free-threading, monorepos, and lazy imports for start-up time.
- Upgrading to Python 3.1555 min · Python 3.15+Plan and carry out an upgrade to Python 3.15: read the porting notes, turn deprecation warnings into errors on the old version, handle the UTF-8 default (PEP 686), the 3.15 removals and behaviour changes, prepare for 3.16 and 3.17, and test across versions, including the free-threaded build.