Skip to content
Free public beta until December 4, 2026: every course is open, no card needed. After that, the first chapter of each course stays free and full access is a one-time $39 ($19 for up to 100 founding members chosen for helpful feedback). Your progress is kept. How the beta works

Language & library reference

Concept clusters →

Every named result in this course, grouped by chapter. Each one links back to where it's derived.

Values, types, and operators

Rule

Floor division rounds down, and the remainder fits it

also known as: // and %, floor division and modulo, divmod

Formal. For integers a and b (with b not zero), a // b is the largest whole number less than or equal to a / b: it rounds toward negative infinity. a % b is defined so that (a // b) * b + a % b == a always holds, which gives the remainder the sign of the divisor b. divmod(a, b) returns both as a pair. A single / is true division and always gives a float.

In plain English. // asks "how many whole times does b fit?", and when the answer isn't whole it always steps DOWN, to the left on the number line, even for negative numbers. % is whatever is left over so that the pieces add back up to a.

Example. 7 // 2 is 3 and 7 % 2 is 1. -7 // 2 is -4, not -3, and -7 % 2 is 1, because -4 * 2 + 1 is -7. 7 / 2 is 3.5, and 8 / 2 is 4.0.

Why this rule exists. Rounding down keeps % useful for clocks, calendars and "every n-th" tests: n % 7 is always 0 to 6 for a positive 7, whatever the sign of n.

Rule

What counts as false

also known as: truthiness, truthy and falsy values

Formal. When Python needs a true-or-false answer from a value (as bool() gives, and as if and while will use in Chapter 4), these are falsy: False, None, zero of any number type (0, 0.0, 0j), and every empty collection: the empty string "" now, and the empty lists, tuples, dicts and sets of Chapter 5 later. Everything else is truthy.

In plain English. Nothing, zero and empty mean false. Anything with something in it means true. A string is judged only by whether it is empty, never by what it says.

Example. bool("") is False, but bool(" "), bool("0") and bool("False") are all True. bool(-1) is True, and bool(0.0) is False.

Why this rule exists. It lets a test say what it means in a few words: a test of name reads as "if a name was typed".

Rule

Python's operator precedence ladder

also known as: order of operations, operator precedence, associativity

Formal. From tightest to loosest: **; unary -x/+x; *, /, //, %; +, -; comparisons (<, <=, ==, !=, in, is, ...); not; and; or. Operators on the same rung group left to right, except **, which groups right to left. ** also binds tighter than a unary minus on its LEFT. Parentheses beat all of it.

In plain English. Powers first, then multiplying and dividing, then adding and subtracting, then comparing, then the logic words. Within one rung, work left to right; the one exception is **, which works right to left.

Example. 2 + 3 * 4 is 14. -3 ** 2 is -9, because it means -(3 ** 2). 10 - 4 - 3 is 3, but 2 ** 3 ** 2 is 2 ** 9, which is 512.

Why this rule exists. It follows the order of operations from school arithmetic, so formulas read the way they're written on paper. When an expression is hard to read, parentheses cost nothing.

Strings and text processing

Rule

Indexes start at 0, and a slice stops before its end

also known as: zero-based indexing, half-open slices, negative indexes

Formal. In a sequence of length n, the items are at indexes 0 to n - 1; a negative index -k means index n - k, so -1 is the last item. The slice s[i:j] contains the items at indexes i, i + 1, ..., j - 1, so it has j - i items. A missing i means the start and a missing j the end. A slice whose bounds run past the end simply stops at the end, while a single index past the end is an IndexError.

In plain English. Count the gaps between characters, starting at 0: a slice takes everything between two gaps. -1 is "the last one", -2 the one before it.

Example. With word = "python": word[0] is 'p', word[-1] is 'n', word[1:4] is 'yth' (three characters, 4 - 1), and word[:2] + word[2:] is the whole word again.

Why this rule exists. Stopping before the end makes lengths and joins add up: s[:k] has exactly k items, and s[:k] + s[k:] is always s.

Decisions and repetition

Rule

range stops before its stop value

also known as: range(start, stop, step), half-open range

Formal. range(stop) produces 0, 1, ..., stop - 1. range(start, stop) produces start, start + 1, ..., stop - 1. range(start, stop, step) adds step each time and stops as soon as the next value would reach or pass stop; a negative step counts down. The stop value itself is never produced, so range(start, stop) has stop - start values.

In plain English. range counts up to, but not including, the stop number. range(5) means five numbers, starting from 0.

Example. range(5) produces 0, 1, 2, 3, 4. range(2, 6) produces 2, 3, 4, 5. range(1, 10, 3) produces 1, 4, 7, and range(5, 0, -1) produces 5, 4, 3, 2, 1.

Why this rule exists. It matches zero-based indexing: for i in range(len(word)) visits exactly the valid indexes of word, and range(n) runs a loop exactly n times.

Collections

Rule

Mutable and immutable types

also known as: mutability, aliasing, shallow copy

Formal. An object of an immutable type (int, float, bool, str, tuple, frozenset, None) can never change; every operation that seems to change it builds a new object. An object of a mutable type (list, dict, set, and most user-defined classes) can be changed in place. Assignment b = a never copies: it binds a second name to the same object, so a change made through one name of a mutable object is seen through every name. a.copy(), list(a) and a[:] make a shallow copy (a new outer object holding the same items); copy.deepcopy(a) copies the nested objects too. Only immutable values (and tuples of them) can be dict keys or set members.

In plain English. Some objects are set in stone and some are whiteboards. Two names can point at the same whiteboard, and then anyone writing on it changes what both see. Stone can't be written on, so sharing it is always safe.

Example. a = [1, 2]; b = a; b.append(3) leaves a as [1, 2, 3]. s = "hi"; t = s; t += "!" leaves s as "hi", because += built a new string for t.

Why this rule exists. Sharing one object is fast and lets a function change a list it was given. Immutability is what makes a value safe to share and to use as a key.

Modules, packages, and project structure

Rule

The virtual-environment workflow

also known as: venv, python -m venv .venv, activate, deactivate

Formal. python -m venv .venv (or python3 -m venv .venv) creates an isolated environment in the folder .venv, built from the Python that ran the command. Activating it puts the environment's bin (Windows: Scripts) folder first on PATH: source .venv/bin/activate on macOS and Linux, .venv\Scripts\Activate.ps1 in PowerShell, .venv\Scripts\activate.bat in Command Prompt. While it is active, python means the environment's own interpreter, and the prompt shows (.venv). deactivate restores PATH. Running the environment's interpreter by its path (.venv/bin/python) uses it without activating. The .venv folder is never committed to version control.

In plain English. Each project gets its own private Python with its own installed packages. Make it once, switch it on in each new terminal, install into it, and switch it off when you're done.

Example. python3 -m venv .venv, then source .venv/bin/activate. The prompt now starts with (.venv), and anything installed from here on belongs to this project only.

Why this rule exists. Two projects often need different versions of the same package. Separate environments keep them from breaking each other and keep the system's own Python untouched.

Exceptions, debugging, and testing

Rule

The everyday built-in exceptions

also known as: NameError, TypeError, ValueError, IndexError, KeyError, ZeroDivisionError, AttributeError

Formal. NameError: a name that was never assigned. TypeError: an operation applied to an object of the wrong type ("3" + 1, len(5)). ValueError: the right type but an unusable value (int("cat")). IndexError: a position past the end of a list or string. KeyError: a missing dict key. ZeroDivisionError: division or % by zero. AttributeError: a method (or other name after a dot) the object doesn't have. FileNotFoundError (a kind of OSError): opening a path that doesn't exist. All of them are subclasses of Exception.

In plain English. The exception's name says what went wrong: a name Python never heard of, the wrong kind of thing, the right kind with a bad value, a position past the end, a missing key, division by zero, or a method the thing doesn't have.

Example. [1, 2, 3][3] raises IndexError; {"a": 1}["b"] raises KeyError; int("4.5") raises ValueError; "total: " + 5 raises TypeError.

Why this rule exists. The last line of a traceback names the exception first, so knowing these seven tells you what kind of mistake to look for before you read anything else.

Networking, APIs, and web data

Rule

The first digit of an HTTP status is its family

also known as: HTTP status code classes, 2xx 3xx 4xx 5xx

Formal. An HTTP status code's first digit is its family: 1xx informational, 2xx success, 3xx redirection, 4xx client error, 5xx server error. A 4xx response won't succeed if the same request is sent again unchanged; a 5xx (and 429 Too Many Requests) may succeed later. Success should be tested as a family, 200 <= code < 300, not as one exact code.

In plain English. Read the first digit first. 2 means it worked, 3 means look somewhere else, 4 means your request was wrong, and 5 means the server went wrong.

Example. 200 OK, 201 Created, 204 No Content; 301 Moved Permanently, 304 Not Modified; 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 429 Too Many Requests; 500 Internal Server Error, 503 Service Unavailable.

Why this rule exists. New codes can be added without breaking old programs, because every program can still act on the family.