diff --git a/docs/collections.md b/docs/collections.md
index 9677bb6..8469a53 100644
--- a/docs/collections.md
+++ b/docs/collections.md
@@ -898,6 +898,13 @@ The **negative index** starts counting down from the end instead, starting at `-
print("invalid format")
```
+- **Swapping variables:** unpack two values into each other's variables in one line, instead of using a temporary variable to hold one during the swap.
+
+ ```python-ref
+ a, b = "ball python", "boa" # a="ball python" b="boa"
+ a, b = b, a # swaps directly — no temporary variable needed — a="boa" b="ball python"
+ ```
+
### Tuple operations { data-card-link="skip" }
#### Inspect
diff --git a/docs/errors.md b/docs/errors.md
index e43fd1e..226cc98 100644
--- a/docs/errors.md
+++ b/docs/errors.md
@@ -6,19 +6,21 @@ description: >-
# :material-bug-outline:{ .lg .middle } Errors
-**Errors** occur when a line of code is impossible to run, so the program stops and displays an error message with information on what went wrong and where.
+**"Errors"** occur when a line of code is impossible to run, so the program stops and displays a message with information on what went wrong and where.
-**"Bugs"** are the general term for *any* mistake or error in your code, like logic errors.
+**"Bugs"** are the general term for errors or *any mistake* in your code, like logic errors.
-They are part of programming, and will happen constantly. Once you know what kind of error you have, you can check for common causes, and try one of these ways of fixing it:
+**"Exceptions"** are Python's formal term for the type of error that was raised, like `KeyError` or `ValueError`.
+
+They are part of programming, and happen constantly. Based on the kind of error, there are different methods to identify and fix them:
-| | [Read traceback/error](#reading-a-traceback) | [`try`/`except`](#catch-with-tryexcept) | [Debugging strategies](#debugging-strategies) | [Debugger tool](#debugger-tool) | [Testing](#detect-errors-with-testing) |
+| | [Read tracebacks/errors](#reading-a-traceback) | [handle with try/except](#catch-with-tryexcept) | [Debugging strategies](#debugging-strategies) | [Debugger tool](#debugger-tool) | [Testing](#detect-errors-with-testing) |
|----------|:---:|:---:|:---:|:---:|:---:|
-| [Syntax errors](#syntax-errors) | :material-check:{ .pt-icon-success } | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } |
-| [Runtime errors](#runtime-errors) | :material-check:{ .pt-icon-success } | :material-check:{ .pt-icon-success } | :material-check:{ .pt-icon-success } | :material-check:{ .pt-icon-success } | :material-check:{ .pt-icon-success } |
-| [Logic errors](#logic-errors) | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } | :material-check:{ .pt-icon-success } | :material-check:{ .pt-icon-success } | :material-check:{ .pt-icon-success } |
+| [**Syntax errors**
(incorrect grammar, can't read file)](#syntax-errors) | [:material-check:{ .pt-icon-success }](#reading-a-traceback) | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } |
+| [**Runtime errors**
(crashes when code can't execute)](#runtime-errors) | [:material-check:{ .pt-icon-success }](#reading-a-traceback) | [:material-check:{ .pt-icon-success }](#catch-with-tryexcept) | [:material-check:{ .pt-icon-success }](#debugging-strategies) | [:material-check:{ .pt-icon-success }](#debugger-tool) | [:material-check:{ .pt-icon-success }](#detect-errors-with-testing) |
+| [**Logic errors**
(runs, but gives unexpected output)](#logic-errors) | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } | [:material-check:{ .pt-icon-success }](#debugging-strategies) | [:material-check:{ .pt-icon-success }](#debugger-tool) | [:material-check:{ .pt-icon-success }](#detect-errors-with-testing) |
@@ -28,13 +30,13 @@ They are part of programming, and will happen constantly. Once you know what kin
### Syntax errors { .pt-fake-h2 }
-The code doesn't follow Python's grammar rules, so it can't read or run the file. These errors must be fixed directly.
+The code doesn't follow Python's grammar rules, so it can't read or run the file. These errors must be fixed directly. These are often incorrect punctuation, spacing, or typos.
-| | [Read error message](#reading-a-syntax-error-message) | [`try`/`except`](#catch-with-tryexcept) | [Debugging strategies](#debugging-strategies) | [Debugger tool](#debugger-tool) | [Testing](#detect-errors-with-testing) |
+| | [Read error message](#reading-a-syntax-error-message) | [handle with try/except](#catch-with-tryexcept) | [Debugging strategies](#debugging-strategies) | [Debugger tool](#debugger-tool) | [Testing](#detect-errors-with-testing) |
|---|:---:|:---:|:---:|:---:|:---:|
-| Ways to fix syntax errors | :material-check:{ .pt-icon-success }
Points to what Python couldn't read | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } |
+| Ways to fix syntax errors | [:material-check:{ .pt-icon-success }
Points to what Python couldn't read](#reading-a-syntax-error-message) | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } | :material-close:{ .pt-icon-fail } |
@@ -54,15 +56,15 @@ The code doesn't follow Python's grammar rules, so it can't read or run the file
### Runtime errors { .pt-fake-h2 }
-A **runtime error** happens once a program is already running — the code is grammatically correct, but unable to execute.
+A **runtime error** crashes when a line of code is impossible to execute. It is grammatically correct so is able to read the file and start running, until it encounters something it can't do so it stops and gives you a specific error name.
Think about what programming concepts the failing line is using (data type, loop, conditional, etc), and revisit that page on this site to confirm you're applying it correctly.
-| | [Read traceback message](#reading-a-traceback) | [`try`/`except`](#catch-with-tryexcept) | [Debugging strategies](#debugging-strategies) | [Debugger tool](#debugger-tool) | [Testing](#detect-errors-with-testing) |
+| | [Read tracebacks](#reading-a-traceback) | [handle with try/except](#catch-with-tryexcept) | [Debugging strategies](#debugging-strategies) | [Debugger tool](#debugger-tool) | [Testing](#detect-errors-with-testing) |
|---|:---:|:---:|:---:|:---:|:---:|
-| Ways to fix runtime errors | :material-check:{ .pt-icon-success }
Tells you exactly where it broke | :material-check:{ .pt-icon-success }
Use when the failure is expected and outside your control | :material-check:{ .pt-icon-success }
Figure out why it failed | :material-check:{ .pt-icon-success }
Step through the code to see exactly what's happening | :material-check:{ .pt-icon-success }
Lock in the fix to prevent it from happening again |
+| Ways to fix runtime errors | [:material-check:{ .pt-icon-success }
Tells you exactly where it broke](#reading-a-traceback) | [:material-check:{ .pt-icon-success }
Use when the failure is expected and outside your control](#catch-with-tryexcept) | [:material-check:{ .pt-icon-success }
Figure out why it failed](#debugging-strategies) | [:material-check:{ .pt-icon-success }
Step through the code to see exactly what's happening](#debugger-tool) | [:material-check:{ .pt-icon-success }
Lock in the fix to prevent it from happening again](#detect-errors-with-testing) |
@@ -70,6 +72,7 @@ Think about what programming concepts the failing line is using (data type, loop
| Kind of runtime error | Happens when | Check for |
|-------|---------------|-----------|
+| **`AssertionError`** | An [`assert`](#assert-a-condition) statement's condition was `False` | - The condition itself is wrong — double check the logic being asserted.
- If this should always be checked, not just during development, use [`raise`](#raise-an-exception) instead — `assert` gets stripped out when Python runs with the `-O flag`.
|
| **`AttributeError`** | Calling a method or attribute that doesn't exist on that object | - Typo in a method name.
- A method that is being called on the wrong type: `"ball".append(...)` fails since `.append()` can only be applied to `list` not `str`.
- A variable holds `None` instead of the object you meant to call a method on — often a function returned None instead of an expected value.
- Reassigning a variable to the result of an in-place list method like `.append()` or `.sort()` — those return `None`, not the changed list.
- A local file named the same as a library you import, see [file naming rules](workspace.md#step-2-write-and-run-a-python-file).
|
| **`FileNotFoundError`** | Trying to open a file that doesn't exist at that path | - A typo in the filename, path, extension, or case. Open your file browser (Finder/ File Explorer) and check directly.
- The path is relative to your current working directory
- Meant to create a file but opened in read mode `"r"` instead of write mode `"w"` which creates the file if it doesn't exist.
|
| **`ImportError`** | Importing a name that doesn't exist in a module that *was* found | - The module itself was found, but `from module import name` is asking for something that doesn't exist inside it — a typo in `name`.
- The name exists, but in a different module than the one you're importing it from.
- A local file named the same as a library you import, see [file naming rules](workspace.md#step-2-write-and-run-a-python-file).
- Two of your own files importing from each other — restructure so one of them doesn't need to import the other, often by moving the shared piece into a third file.
|
@@ -97,9 +100,9 @@ Think about what programming concepts you are using (data types, loops, conditio
-| | [Read traceback/error](#reading-a-traceback) | [`try`/`except`](#catch-with-tryexcept) | [Debugging strategies](#debugging-strategies) | [Debugger tool](#debugger-tool) | [Testing](#detect-errors-with-testing) |
+| | [Read tracebacks/errors](#reading-a-traceback) | [handle with try/except](#catch-with-tryexcept) | [Debugging strategies](#debugging-strategies) | [Debugger tool](#debugger-tool) | [Testing](#detect-errors-with-testing) |
|---|:---:|:---:|:---:|:---:|:---:|
-| Ways to fix logic errors? | :material-close:{ .pt-icon-fail }
No error message is shown | :material-close:{ .pt-icon-fail }
No error is raised | :material-check:{ .pt-icon-success }
Helps you find exactly where the code's behavior diverges from what you expected | :material-check:{ .pt-icon-success }
Especially useful, since there's no error message to point you anywhere | :material-check:{ .pt-icon-success }
State your expected output, so the mistake gets caught automatically next time |
+| Ways to fix logic errors? | :material-close:{ .pt-icon-fail }
No error message is shown | :material-close:{ .pt-icon-fail }
No error is raised | [:material-check:{ .pt-icon-success }
Helps you find exactly where the code's behavior diverges from what you expected](#debugging-strategies) | [:material-check:{ .pt-icon-success }
See what is happening line by line](#debugger-tool) | [:material-check:{ .pt-icon-success }
State your expected output, so the mistake gets caught automatically next time](#detect-errors-with-testing) |
@@ -159,79 +162,6 @@ IndexError: list index out of range
-### Catch with try/except { .pt-fake-h2 }
-
-`try`/`except` lets your program handle [runtime errors](#runtime-errors) and then continue without crashing.
-
-```python-ref
-try:
- [run this block of code first] # only the line(s) that could cause the error
-except [error name]: # i.e. KeyError, ValueError, etc
- [if the try block caused the specified error, then continue and run this code]
-```
-
-#### When to use it { .pt-fake-h3 }
-
-!!! success "Handle it with try/except"
- - The failure is genuinely outside your control — a file that might not exist, a network call, user input you can't fully validate ahead of time
- - The failure is an expected, normal outcome — not a mistake
- - You have real alternative logic to run instead, like a fallback value or a retry — not just silencing the error
-
-!!! danger "Fix the code instead"
- - You don't know what is causing the error
- - It is in your control to fix the error
- - Just wanting to make errors stop — often a sign there is a bug
-
-#### Catching multiple exceptions { .pt-fake-h3 }
-
-List several exception types in one `except` to handle them the same way. Separate `except` blocks work too, if each error type needs different handling — Python checks them top to bottom and runs the first one that matches.
-
-```python-ref
-try:
- length = float(lengths[species])
-except (KeyError, TypeError): # handle these the same way
- print("couldn't look up that species")
-except ValueError: # separate for different handling
- print("length on record isn't a number")
-```
-
-#### Optional else and finally blocks { .pt-fake-h3 }
-
-`else` runs only if `try` succeeded, but it won't trigger the `except` block. This isn't commonly used.
-
-`finally` always runs, and is for cleanup that has to happen either way, like closing a file.
-
-
-```python-ref
-try:
- length = lengths[species] # attempted first
-except KeyError:
- print("no length on record") # runs only on a KeyError
-else:
- print(f"found it: {length} ft") # runs only if try succeeded
-finally:
- print("lookup attempt finished") # always runs
-```
-
-??? run "Run a try/except example"
- A case where try/except is the right tool — converting a value that might not be a valid number:
-
- ```python
- raw_length = "n/a"
-
- print("trying to read the length")
-
- try:
- length = float(raw_length)
- print(f"length: {length} ft")
- except ValueError:
- print(f"couldn't read '{raw_length}' as a number")
- ```
-
-
-
-
-
### Debugging strategies { .pt-fake-h2 }
These general techniques help close the gap between what you think the code does and what it's actually doing.
@@ -374,3 +304,150 @@ They're also useful for [runtime errors](#runtime-errors) — a test can exercis
+## Handling errors:
+
+
+
+### Catch with try/except { .pt-fake-h2 }
+
+`try`/`except` lets your program handle [runtime errors](#runtime-errors) and then continue without crashing.
+
+```python-ref
+try:
+ [run this block of code first] # only the line(s) that could cause the error
+except [error name]: # i.e. KeyError, ValueError, etc
+ [if the try block caused the specified error, then continue and run this code]
+```
+
+!!! success "Handle it with try/except"
+ - The failure is genuinely outside your control — a file that might not exist, a network call, user input you can't fully validate ahead of time
+ - The failure is an expected, normal outcome — not a mistake
+ - You have real alternative logic to run instead, like a fallback value or a retry — not just silencing the error
+
+!!! danger "Fix the code instead"
+ - You don't know what is causing the error
+ - It is in your control to fix the error
+ - Just wanting to make errors stop — often a sign there is a bug
+
+#### Catch specific exceptions { .pt-fake-h3 }
+
+Catch the exact exception you expect (`except ValueError:`) instead of a bare `except:` — a bare `except` also silently swallows errors you didn't anticipate, including a typo in your own code, and even catches things like a keyboard interrupt (++ctrl+c++) that usually shouldn't be caught at all.
+
+```python-ref
+try:
+ length_ft = float(user_input)
+except ValueError: # only catches what you actually expect
+ print("invalid input")
+```
+
+List several exception types in one `except` to handle them the same way. Separate `except` blocks work too, if each error type needs different handling — Python checks them top to bottom and runs the first one that matches.
+
+```python-ref
+try:
+ length = float(lengths[species])
+except (KeyError, TypeError): # handle these the same way
+ print("couldn't look up that species")
+except ValueError: # separate for different handling
+ print("length on record isn't a number")
+```
+
+#### finally { .pt-fake-h3 }
+
+`finally` is an optional block that always runs after `try`/`except`, whether or not an exception happened — used for cleanup that has to happen either way, like closing a file.
+
+```python-ref
+try:
+ length = lengths[species] # attempted first
+except KeyError:
+ print("no length on record") # runs only on a KeyError
+finally:
+ print("lookup attempt finished") # always runs
+```
+
+??? tip "Optional`else` block that runs if `try` succeeded"
+ `else` runs only if `try` succeeded, and won't trigger the `except` block if it fails — useful for keeping code that should only run on success out of the `try` block itself, so a bug in it doesn't get wrongly caught by the same `except`.
+
+ ```python-ref
+ try:
+ length = lengths[species] # attempted first
+ except KeyError:
+ print("no length on record") # runs only on a KeyError
+ else:
+ print(f"found it: {length} ft") # runs only if try succeeded
+ ```
+
+??? run "Run a try/except example"
+ A case where try/except is the right tool — converting a value that might not be a valid number:
+
+ ```python
+ raw_length = "n/a"
+
+ print("trying to read the length")
+
+ try:
+ length = float(raw_length)
+ print(f"length: {length} ft")
+ except ValueError:
+ print(f"couldn't read '{raw_length}' as a number")
+ ```
+
+
+
+
+
+### Raise an exception { .pt-fake-h2 }
+
+**`Raise` triggers an exception yourself,** instead of waiting for one to happen naturally — useful for stopping bad input or state before it causes a more confusing error later.
+
+```python-ref
+def set_length(length_ft):
+ if length_ft < 0:
+ raise ValueError("length can't be negative")
+ return length_ft
+```
+
+Whoever calls the code with `raise` can then put it inside a [`try`/`except`](#catch-with-tryexcept) and handle it gracefully:
+
+```python-ref
+try:
+ set_length(-2)
+except ValueError as e:
+ print(e)
+```
+
+
+
+
+
+### Assert a condition { .pt-fake-h2 }
+
+**`assert` raises an `AssertionError` if a condition is False** — the same idea as `raise`, but meant for checking your own assumptions while you're still writing and testing the code, not for validating things that need to be checked every time the program is run. Catching a wrong assumption immediately, with a traceback pointing at it, is easier to debug than discovering it later as a [logic error](#logic-errors).
+
+```python-ref
+assert [boolean expression] # raises AssertionError if condition is False
+assert [boolean expression], [message] # can add an optional message
+```
+
+```python-ref
+assert length_ft > 0, "length should be positive"
+```
+
+If `length_ft` is `-1`, that line raises `AssertionError`, with `message` as the text:
+
+```python-ref
+Traceback (most recent call last):
+ File "lengths.py", line 1, in
+AssertionError: length should be positive
+```
+
+Like any other exception, `AssertionError` can be caught with [`try`/`except`](#catch-with-tryexcept):
+
+```python-ref
+try:
+ assert length_ft > 0, "length should be positive"
+except AssertionError as e:
+ print(e)
+```
+
+
+
diff --git a/docs/foundations.md b/docs/foundations.md
index 1a96c4b..ef83018 100644
--- a/docs/foundations.md
+++ b/docs/foundations.md
@@ -144,6 +144,31 @@ print(4.5) # when printing a number, you do not need quotes
Most sections on this site end with a collapsed block like the one below — open it, click **Run**, and try editing the code and running it again.
+### Escape sequences
+
+`\n` and `\t` are **escape sequences** — `\n` inserts a line break, `\t` a tab — so a single `print()` call can space out multi-line or columned output.
+
+```python
+print(f"species: burmese\nlength: 10 ft\n")
+print("species\t\tlength_ft")
+print("ball python\t4.5")
+```
+
+`\"`, `\'`, and `\\` escape a character that would otherwise end the string early or be read as another backslash — a quote matching the one the string started with, or a literal backslash itself.
+
+```python
+print("she said \"hello\"") # a double quote inside a double-quoted string
+print('it\'s a python') # a single quote inside a single-quoted string
+print("C:\\snakes") # a literal backslash
+```
+
+A row of repeated characters makes a quick visual separator between sections of console output, useful for breaking up a long script's output into readable chunks.
+
+```python
+print("survey results")
+print("=" * 40)
+```
+
### Going further { data-card-link="skip" }
??? run "Run a print() example"
@@ -152,6 +177,20 @@ Most sections on this site end with a collapsed block like the one below — ope
```python
print("hello, field guide")
print(4.5)
+
+
+ print(f"species: burmese\nlength: 10 ft\n")
+ print("species\t\tlength_ft")
+ print("ball python\t4.5")
+
+
+ print("she said \"hello\"")
+ print('it\'s a python')
+ print("C:\\snakes")
+
+
+ print("survey results")
+ print("=" * 40)
```
diff --git a/docs/functions.md b/docs/functions.md
index 613f60c..e4ebd7b 100644
--- a/docs/functions.md
+++ b/docs/functions.md
@@ -1,7 +1,7 @@
---
description: >-
Python functions explained with runnable examples: defining, calling, arguments,
- *args/**kwargs, scope, and recursion.
+ *args/**kwargs, scope, recursion, and decorators.
---
# :material-function-variant:{ .lg .middle } Functions
@@ -111,6 +111,54 @@ def describe(species, length_ft=5, venomous=False):
describe(species="ball", venomous=True) # length_ft still uses its default
```
+### Type hints
+
+A **type hint** annotates a parameter or return value with the type it's expected to be — `species: str`, `length_ft: float`, `-> bool` — without Python enforcing it at runtime; it's documentation an editor or a separate type checker (like `mypy`) can check for you.
+
+```python-ref
+def is_unusually_long(species: str, length_ft: float) -> bool:
+ return length_ft > 5
+```
+
+A wrong type still runs — Python doesn't stop you from calling `is_unusually_long("ball python", "4.5")` with a string instead of a `float` — the hint only helps a tool catch the mismatch before you do, and helps a reader (or their editor) see what's expected without reading the function body.
+
+### Keep functions focused
+
+A function should do one thing. If you find yourself describing it with "and" — "loads the species *and* saves it *and* prints a summary" — it's probably three functions.
+
+```python-ref
+def load_and_describe(species): # doing too much
+ ...
+
+def load_species(species): # one job each
+ ...
+
+def describe(species):
+ ...
+```
+
+Repeating the same few lines in multiple places is a sign to pull them into their own function instead — commonly called **DRY** ("don't repeat yourself"). It also means a fix only has to happen in one place, instead of every place the lines were copied to.
+
+??? tip "Guard clauses: return early instead of nesting"
+ Handle the exception case first and return, rather than wrapping the rest of the function in an `else`. It keeps the normal path at the lowest indentation level, instead of nested one level deeper for every added check.
+
+ ```python-ref
+ def describe(length_ft):
+ if length_ft > 0:
+ return f"{length_ft} ft"
+ else:
+ return "unknown length"
+ ```
+
+ ```python-ref
+ def describe(length_ft):
+ if length_ft <= 0:
+ return "unknown length"
+ return f"{length_ft} ft"
+ ```
+
+ Both versions do the same thing — the second reads top to bottom without having to track which `if` branch you're inside.
+
### Going further { data-card-link="skip" }
??? tip "pass placeholder"
@@ -199,6 +247,12 @@ describe(species="ball", venomous=True) # length_ft still uses its default
print(describe("ball", venomous=True))
print(describe(species="burmese", length_ft=12))
+
+
+ def is_unusually_long(species: str, length_ft: float) -> bool:
+ return length_ft > 5
+
+ print(is_unusually_long("ball python", 6))
```
@@ -404,3 +458,187 @@ Every recursive function needs two parts:
+
+
+## Decorators
+
+**`@decorator`** lets you add behavior to a function without editing the function's own code — write the behavior once, then apply it to as many functions as you want. It's written as `@decorator_name`, placed directly above a `def`, and takes one function in, returning a function out[^callable].
+
+### Wrapping the call
+
+A decorator can run its own code around a function call by returning a different function instead of the original — a **wrapper** that does something, calls the original, then returns. This is the shape behind most decorators you'll actually use — logging, timing, or checking permissions before letting a call through.
+
+```python
+def decorator_name(func): # func is the function being decorated (here it's "describe()")
+ def wrapper(): # desfines a new function that runs in place of func from now on
+ print("looking up a snake...")
+ func() # calls the original, still reachable through func
+ print("found it")
+ return wrapper # decorator returns the new function name
+
+@decorator_name # decorator_name can be any name you pick
+def describe(): # here is your regular function you are decorating
+ print("a python")
+
+describe() # every function call now prints "looking up a snake...", "a python", then "found it"
+```
+
+`@decorator_name` reassigns `describe` to `wrapper.` Calling `describe()` now actually runs `wrapper()`, which calls the original through `func`. `wrapper` and `decorator_name` are just names, not special syntax — any valid identifier works for either one.
+
+### Returning the original function
+
+Not every decorator needs a wrapper — the only actual requirement is returning *some* function. `catalog` below doesn't define a new one at all, it just hands back `func` itself, unchanged, so its surrounding prints only run once, the moment `describe` is defined — never again on any later call to `describe()`.
+
+```python
+def catalog(func):
+ print("looking up a snake...")
+ func()
+ print("found it")
+ return func # func, not func() — a reference to the function, not a call to it
+
+@catalog
+def describe():
+ print("a python")
+
+describe() # "a python" only — the surrounding prints already ran once, at decoration
+
+@catalog
+def count():
+ return 5
+
+print(count()) # 5 — return values pass through untouched too
+```
+
+`@catalog` reassigns `describe` to whatever `catalog` returns. `return func()` would call it and hand back its result instead of the function itself — `None` here — breaking `describe` as something you can call again.
+
+### Accepting arguments
+
+`describe` above takes no arguments, so `wrapper` didn't need to accept any either. Most functions do take arguments — `describe` normally takes a `species`, for instance — and `wrapper` has to accept whatever the decorated function needs.
+
+`*args` and `**kwargs` let `wrapper` accept anything and print exactly what came in — useful for seeing what a function was actually called with, whatever its shape:
+
+```python
+def decorator(func):
+ def wrapper(*args, **kwargs): # accepts any parameters, instead of a fixed signature
+ print(f"called with {args}") # shows exactly what was passed in
+ return func(*args, **kwargs) # then forwards it all to func
+ return wrapper
+
+@decorator
+def describe(species):
+ return f"a {species} python"
+
+print(describe("ball")) # prints "called with ('ball',)", then "a ball python"
+
+@decorator
+def total_length(*lengths):
+ return sum(lengths)
+
+print(total_length(5, 12, 8)) # prints "called with (5, 12, 8)", then 25 — same decorator, different signature
+```
+
+### Advanced uses
+
+??? tip "Decorators with arguments"
+ A decorator that needs its own settings takes those arguments one level out — a function that *returns* a decorator, instead of being one directly. This is how a decorator like Flask's `@app.route("/users")` gets its own argument (the URL path), separate from whatever function it ends up decorating.
+
+ ```python
+ def tag(label): # called first, with the decorator's own argument
+ def decorator(func): # this is the actual decorator tag(label) builds
+ def wrapper(*args, **kwargs):
+ print(f"[{label}]")
+ return func(*args, **kwargs)
+ return wrapper
+ return decorator
+
+ @tag("sighting")
+ def describe(species):
+ return f"a {species} python"
+
+ print(describe("ball"))
+ ```
+
+??? tip "Applying a decorator manually"
+ `@decorator_name` is shorthand for calling the decorator directly and reassigning the function's name yourself — the two lines below have the exact same effect as `@decorator` above a `def describe():`.
+
+ ```python
+ def decorator(func):
+ def wrapper():
+ print("looking up a snake...")
+ func()
+ print("found it")
+ return wrapper
+
+ def describe():
+ print("a python")
+
+ describe = decorator(describe) # same effect as @decorator, written out by hand
+ describe() # prints "looking up a snake...", "a python", then "found it"
+ ```
+
+ Writing it out by hand is useful when a function shouldn't always be decorated — `@` applies unconditionally, every time the function is defined, while the manual form can sit behind a condition:
+
+ ```python
+ def decorator(func):
+ def wrapper():
+ print("looking up a snake...")
+ func()
+ print("found it")
+ return wrapper
+
+ debug = True
+
+ def describe():
+ print("a python")
+
+ if debug:
+ describe = decorator(describe) # only decorated when debug is True
+
+ describe()
+ ```
+
+??? note "Stacking decorators"
+ Multiple decorators on the same function apply bottom-up — the one closest to `def` wraps first, and each one after it wraps the result of the one before. Stacking is common whenever a function needs more than one independent behavior — a web view that's both registered at a URL and requires the user to be logged in, for instance.
+
+ ```python
+ def bold(func):
+ def wrapper(*args, **kwargs):
+ return f"**{func(*args, **kwargs)}**" # wraps whatever it's given in **
+ return wrapper
+
+ def shout(func):
+ def wrapper(*args, **kwargs):
+ return func(*args, **kwargs).upper() # uppercases whatever it's given
+ return wrapper
+
+ @bold
+ @shout
+ def describe(species):
+ return f"a {species} python"
+
+ print(describe("ball")) # shout wraps describe first, then bold wraps shout's result
+ ```
+
+??? tip "Preserving identity"
+ Decorating a function replaces its identity with the wrapper's — `describe.__name__` becomes `"wrapper"`, not `"describe"`, since Python only sees the function `decorator` returned. `functools.wraps` copies the original function's name, docstring, and other metadata onto the wrapper so introspection tools still see the right name. This matters beyond cosmetics — some frameworks (Flask included) use a decorated function's `__name__` internally, so skipping `@wraps` can break things that have nothing to do with printing a name.
+
+ ```python
+ from functools import wraps
+
+ def decorator(func):
+ @wraps(func) # copies func's __name__, __doc__, etc. onto wrapper
+ def wrapper(*args, **kwargs):
+ return func(*args, **kwargs)
+ return wrapper
+
+ @decorator
+ def describe(species):
+ return f"a {species} python"
+
+ print(describe.__name__) # "describe" — without @wraps(func), this would be "wrapper" instead
+ ```
+
+[^callable]: Technically a decorator just needs to return something *callable* — every decorator on this page returns a function specifically, but not all decorators do. [Classes](oop.md#method-decorators)' built-in `@property`, `@staticmethod`, and `@classmethod` return other kinds of callable object instead.
+
+
+
diff --git a/docs/index.md b/docs/index.md
index ed3f954..e56fa10 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -20,27 +20,51 @@ hide:
Write Python on your computer.
- [**`install`**](workspace.md#step-0-install-python)
-
- [**`write and run .py file`**](workspace.md#step-2-write-and-run-a-python-file)
-
- [**`code editors`**](workspace.md#step-1-pick-an-application-to-write-code-in)
-
- [**`Terminal application`**](workspace.md#using-the-terminal-optional)
-
- [**`virtual environments`**](workspace.md#virtual-environments-optional)
+ [**`install`**](workspace.md#step-0-install-python):
+ [`download`](workspace.md#step-0-install-python)
+ [`version`](workspace.md#step-0-install-python)
+
+ [**`code editors`**](workspace.md#step-1-pick-an-application-to-write-code-in):
+ [`IDLE`](workspace.md#step-1-pick-an-application-to-write-code-in)
+ [`Pycharm`](workspace.md#step-1-pick-an-application-to-write-code-in)
+ [`Thonny`](workspace.md#step-1-pick-an-application-to-write-code-in)
+ [`VS Code`](workspace.md#step-1-pick-an-application-to-write-code-in)
+
+ [**`how to write and run .py file`**](workspace.md#step-2-write-and-run-a-python-file):
+ [`file naming`](workspace.md#step-2-write-and-run-a-python-file)
+
+ [**`Terminal`**](workspace.md#using-the-terminal-optional):
+ [`cd`](workspace.md#using-the-terminal-optional)
+ [`ls`](workspace.md#using-the-terminal-optional)
+ [`pwd`](workspace.md#using-the-terminal-optional)
+ [`shortcuts`](workspace.md#using-the-terminal-optional)
+
+ [**`virtual environments`**](workspace.md#virtual-environments-optional):
+ [`activate`](workspace.md#virtual-environments-optional)
+ [`pip`](workspace.md#virtual-environments-optional)
+ [`requirements.txt`](workspace.md#virtual-environments-optional)
+ [`venv`](workspace.md#virtual-environments-optional)
- :material-cube-outline:{ .lg .middle } [__Foundations__](foundations.md)
Storing, displaying, and inputting values.
- [**`variables`**](foundations.md#variables): [`naming`](foundations.md#naming-variables) [`printing`](foundations.md#printing-variables) [`reassigning`](foundations.md#reassigning-a-variable) [`types`](foundations.md#variables-and-types)
+ [**`variables`**](foundations.md#variables):
+ [`naming`](foundations.md#naming-variables)
+ [`printing`](foundations.md#printing-variables)
+ [`reassigning`](foundations.md#reassigning-a-variable)
+ [`types`](foundations.md#variables-and-types)
- [**`print`**](foundations.md#print-function)
+ [**`print`**](foundations.md#print-function):
+ [`escape sequences`](foundations.md#escape-sequences)
[**`input`**](foundations.md#input-function)
- [**`comments`**](foundations.md#comments): [`"""`](foundations.md#multi-line-comments-with) [`#`](foundations.md#single-line-comments-with) [`FIXME`](foundations.md#single-line-comments-with) [`TODO`](foundations.md#single-line-comments-with)
+ [**`comments`**](foundations.md#comments):
+ [`"""`](foundations.md#multi-line-comments-with)
+ [`#`](foundations.md#single-line-comments-with)
+ [`FIXME`](foundations.md#single-line-comments-with)
+ [`TODO`](foundations.md#single-line-comments-with)
[**`tips for getting started`**](foundations.md#tips-for-getting-started)
@@ -56,31 +80,155 @@ hide:
Kinds of values, and what you can do with them.
- [`isinstance`](types.md) [`type`](types.md)
-
- [**`integers`**](types.md#integers): [`+ - * / **`](types.md#arithmetic) [`+= -= *= /= //= %= **=`](types.md#apply-arithmetic-to-a-variable) [`// % divmod`](types.md#floor-division-modulo) [`abs`](types.md#absolute-value) [`boolean expressions`](types.md#boolean-expressions) [`int`](types.md#convert)
-
- [**`floats`**](types.md#floats): [`+ - * / **`](types.md#arithmetic_1) [`+= -= *= /= //= %= **=`](types.md#apply-arithmetic-to-a-variable_1) [`// % divmod`](types.md#floor-division-modulo_1) [`abs`](types.md#adjust) [`boolean expressions`](types.md#boolean-expressions_1) [`float`](types.md#convert_1) [`round`](types.md#adjust)
-
- [**`strings`**](types.md#strings): [`+ * += *=`](types.md#combine) [`boolean expressions`](types.md#boolean-expressions_2) [`capitalize`](types.md#modify) [`combine`](types.md#combine) [`count`](types.md#search) [`endswith`](types.md#validate) [`f-string`](types.md#f-strings) [`find`](types.md#search) [`format`](types.md#f-strings) [`in`](types.md#search) [`index`](types.md#access-characters) [`isalpha`](types.md#validate) [`isdigit`](types.md#validate) [`join`](types.md#combine) [`len`](types.md#inspect) [`lower`](types.md#modify) [`replace`](types.md#modify) [`slice`](types.md#access-characters) [`split`](types.md#convert_2) [`startswith`](types.md#validate) [`step`](types.md#access-characters) [`str`](types.md#convert_2) [`strip`](types.md#modify) [`title`](types.md#modify) [`upper`](types.md#modify)
-
- [**`booleans`**](types.md#booleans): [`== != > < >= <=`](types.md#boolean-expressions_3) [`and`](types.md#logical-operators) [`in`](types.md#boolean-expressions_3) [`is`](types.md#boolean-expressions_3) [`not`](types.md#logical-operators) [`or`](types.md#logical-operators)
-
- [**`None`**](types.md#none): [`boolean expressions`](types.md#boolean-expressions_4) [`is`](types.md#check-for-none) [`is not`](types.md#check-for-none)
+ [`isinstance`](types.md)
+ [`type`](types.md)
+
+ [**`integers`**](types.md#integers):
+ [`+ - * / **`](types.md#arithmetic)
+ [`+= -= *= /= //= %= **=`](types.md#apply-arithmetic-to-a-variable)
+ [`// % divmod`](types.md#floor-division-modulo)
+ [`abs`](types.md#absolute-value)
+ [`boolean expressions`](types.md#boolean-expressions)
+ [`int`](types.md#convert)
+
+ [**`floats`**](types.md#floats):
+ [`+ - * / **`](types.md#arithmetic_1)
+ [`+= -= *= /= //= %= **=`](types.md#apply-arithmetic-to-a-variable_1)
+ [`// % divmod`](types.md#floor-division-modulo_1)
+ [`abs`](types.md#adjust)
+ [`boolean expressions`](types.md#boolean-expressions_1)
+ [`float`](types.md#convert_1)
+ [`round`](types.md#adjust)
+
+ [**`strings`**](types.md#strings):
+ [`+ * += *=`](types.md#combine)
+ [`boolean expressions`](types.md#boolean-expressions_2)
+ [`capitalize`](types.md#modify)
+ [`combine`](types.md#combine)
+ [`count`](types.md#search)
+ [`endswith`](types.md#validate)
+ [`f-string`](types.md#f-strings)
+ [`find`](types.md#search)
+ [`format`](types.md#f-strings)
+ [`in`](types.md#search)
+ [`index`](types.md#access-characters)
+ [`isalpha`](types.md#validate)
+ [`isdigit`](types.md#validate)
+ [`join`](types.md#combine)
+ [`len`](types.md#inspect)
+ [`lower`](types.md#modify)
+ [`replace`](types.md#modify)
+ [`slice`](types.md#access-characters)
+ [`split`](types.md#convert_2)
+ [`startswith`](types.md#validate)
+ [`step`](types.md#access-characters)
+ [`str`](types.md#convert_2)
+ [`strip`](types.md#modify)
+ [`title`](types.md#modify)
+ [`upper`](types.md#modify)
+
+ [**`booleans`**](types.md#booleans):
+ [`== != > < >= <=`](types.md#boolean-expressions_3)
+ [`and`](types.md#logical-operators)
+ [`in`](types.md#boolean-expressions_3)
+ [`is`](types.md#boolean-expressions_3)
+ [`not`](types.md#logical-operators)
+ [`or`](types.md#logical-operators)
+
+ [**`None`**](types.md#none):
+ [`boolean expressions`](types.md#boolean-expressions_4)
+ [`is`](types.md#check-for-none)
+ [`is not`](types.md#check-for-none)
- :material-basket-outline:{ .lg .middle } [__Collections__](collections.md)
Multiple related values grouped into one container.
- [`isinstance`](collections.md) [`type`](collections.md)
-
- [**`lists`**](collections.md#lists): [`+`](collections.md#create) [`append`](collections.md#add-item) [`boolean expressions`](collections.md#boolean-expressions) [`clear`](collections.md#remove-item) [`comprehension`](collections.md#list-comprehension) [`copy`](collections.md#create) [`count`](collections.md#inspect) [`create`](collections.md#create-a-list) [`del`](collections.md#remove-item) [`extend`](collections.md#add-item) [`in`](collections.md#boolean-expressions) [`index`](collections.md#create-a-list) [`index`](collections.md#inspect) [`insert`](collections.md#add-item) [`item`](collections.md#lists) [`len`](collections.md#inspect) [`list`](collections.md#create) [`loop`](collections.md#loop-through-a-list) [`max`](collections.md#arithmetic) [`min`](collections.md#arithmetic) [`pop`](collections.md#remove-item) [`remove`](collections.md#remove-item) [`reverse`](collections.md#sort) [`slice`](collections.md#access-and-update-items) [`sort`](collections.md#sort) [`sorted`](collections.md#sort) [`step`](collections.md#access-and-update-items) [`sum`](collections.md#arithmetic)
-
- [**`dictionaries`**](collections.md#dictionaries): [`access a value`](collections.md#access-a-value) [`boolean expressions`](collections.md#boolean-expressions_1) [`clear`](collections.md#remove_1) [`copy`](collections.md#create_1) [`del`](collections.md#remove_1) [`dict`](collections.md#create_1) [`get`](collections.md#dictionary-operations) [`items`](collections.md#loop-through-a-dictionary) [`key`](collections.md#dictionaries) [`len`](collections.md#inspect_1) [`loop`](collections.md#loop-through-a-dictionary) [`pop`](collections.md#remove_1) [`popitem`](collections.md#remove_1) [`update`](collections.md#update_1) [`value`](collections.md#dictionaries) [`values`](collections.md#loop-through-a-dictionary)
-
- [**`tuples`**](collections.md#tuples): [`access items`](collections.md#access-items) [`boolean expressions`](collections.md#boolean-expressions_2) [`count`](collections.md#inspect_2) [`immmutable`](collections.md#tuples) [`index`](collections.md#tuples) [`index`](collections.md#inspect_2) [`len`](collections.md#inspect_2) [`loop`](collections.md#loop-through-a-tuple) [`max`](collections.md#arithmetic_1) [`min`](collections.md#arithmetic_1) [`packing`](collections.md#packing-and-unpacking) [`sum`](collections.md#arithmetic_1) [`tuple`](collections.md#create_2) [`unpacking`](collections.md#packing-and-unpacking)
-
- [**`sets`**](collections.md#sets): [`add`](collections.md#update_1) [`boolean expressions`](collections.md#boolean-expressions_3) [`clear`](collections.md#remove_1) [`copy`](collections.md#create_3) [`discard`](collections.md#remove_1) [`isdisjoint`](collections.md#compare) [`issubset`](collections.md#compare) [`issuperset`](collections.md#compare) [`len`](collections.md#inspect_3) [`loop`](collections.md#loop-through-a-set) [`max`](collections.md#arithmetic_2) [`min`](collections.md#arithmetic_2) [`pop`](collections.md#remove_1) [`remove`](collections.md#remove_1) [`set`](collections.md#create_3) [`sum`](collections.md#arithmetic_2) [`update`](collections.md#update_1) [`| & - ^`](collections.md#combine)
+ [`isinstance`](collections.md)
+ [`type`](collections.md)
+
+ [**`lists`**](collections.md#lists):
+ [`+`](collections.md#create)
+ [`append`](collections.md#add-item)
+ [`boolean expressions`](collections.md#boolean-expressions)
+ [`clear`](collections.md#remove-item)
+ [`comprehension`](collections.md#list-comprehension)
+ [`copy`](collections.md#create)
+ [`count`](collections.md#inspect)
+ [`create`](collections.md#create-a-list)
+ [`del`](collections.md#remove-item)
+ [`extend`](collections.md#add-item)
+ [`in`](collections.md#boolean-expressions)
+ [`index`](collections.md#create-a-list)
+ [`insert`](collections.md#add-item)
+ [`item`](collections.md#lists)
+ [`len`](collections.md#inspect)
+ [`list`](collections.md#create)
+ [`loop`](collections.md#loop-through-a-list)
+ [`max`](collections.md#arithmetic)
+ [`min`](collections.md#arithmetic)
+ [`pop`](collections.md#remove-item)
+ [`remove`](collections.md#remove-item)
+ [`reverse`](collections.md#sort)
+ [`slice`](collections.md#access-and-update-items)
+ [`sort`](collections.md#sort)
+ [`sorted`](collections.md#sort)
+ [`step`](collections.md#access-and-update-items)
+ [`sum`](collections.md#arithmetic)
+
+ [**`dictionaries`**](collections.md#dictionaries):
+ [`access a value`](collections.md#access-a-value)
+ [`boolean expressions`](collections.md#boolean-expressions_1)
+ [`clear`](collections.md#remove_1)
+ [`copy`](collections.md#create_1)
+ [`del`](collections.md#remove_1)
+ [`dict`](collections.md#create_1)
+ [`get`](collections.md#dictionary-operations)
+ [`items`](collections.md#loop-through-a-dictionary)
+ [`key`](collections.md#dictionaries)
+ [`len`](collections.md#inspect_1)
+ [`loop`](collections.md#loop-through-a-dictionary)
+ [`pop`](collections.md#remove_1)
+ [`popitem`](collections.md#remove_1)
+ [`update`](collections.md#update_1)
+ [`value`](collections.md#dictionaries)
+ [`values`](collections.md#loop-through-a-dictionary)
+
+ [**`tuples`**](collections.md#tuples):
+ [`access items`](collections.md#access-items)
+ [`boolean expressions`](collections.md#boolean-expressions_2)
+ [`count`](collections.md#inspect_2)
+ [`immmutable`](collections.md#tuples)
+ [`index`](collections.md#tuples)
+ [`index`](collections.md#inspect_2)
+ [`len`](collections.md#inspect_2)
+ [`loop`](collections.md#loop-through-a-tuple)
+ [`max`](collections.md#arithmetic_1)
+ [`min`](collections.md#arithmetic_1)
+ [`packing`](collections.md#packing-and-unpacking)
+ [`sum`](collections.md#arithmetic_1)
+ [`tuple`](collections.md#create_2)
+ [`unpacking`](collections.md#packing-and-unpacking)
+
+ [**`sets`**](collections.md#sets):
+ [`add`](collections.md#update_1)
+ [`boolean expressions`](collections.md#boolean-expressions_3)
+ [`clear`](collections.md#remove_1)
+ [`copy`](collections.md#create_3)
+ [`discard`](collections.md#remove_1)
+ [`isdisjoint`](collections.md#compare)
+ [`issubset`](collections.md#compare)
+ [`issuperset`](collections.md#compare)
+ [`len`](collections.md#inspect_3)
+ [`loop`](collections.md#loop-through-a-set)
+ [`max`](collections.md#arithmetic_2)
+ [`min`](collections.md#arithmetic_2)
+ [`pop`](collections.md#remove_1)
+ [`remove`](collections.md#remove_1)
+ [`set`](collections.md#create_3)
+ [`sum`](collections.md#arithmetic_2)
+ [`update`](collections.md#update_1)
+ [`| & - ^`](collections.md#combine)
@@ -92,25 +240,55 @@ hide:
- :material-source-branch:{ .lg .middle } [__Conditionals__](conditionals.md)
- Make decisions about when to run different sections of code.
+ Decision points that run code only if a condition is met.
- [**`if, elif, else`**](conditionals.md#if-elif-else): [`and, or, not`](conditionals.md#logical-operators) [`boolean expressions`](conditionals.md#boolean-expressions)
+ [**`if, elif, else`**](conditionals.md#if-elif-else):
+ [`and, or, not`](conditionals.md#logical-operators)
+ [`boolean expressions`](conditionals.md#boolean-expressions)
- [**`match, case`**](conditionals.md#match-case): [`_ wildcard`](conditionals.md#default-value-_) [`case + if`](conditionals.md#case-if) [`match with |`](conditionals.md#match-multiple-values-with) [`unpacking`](conditionals.md#unpacking-a-tuple)
+ [**`match, case`**](conditionals.md#match-case):
+ [`_ wildcard`](conditionals.md#default-value-_)
+ [`case + if`](conditionals.md#case-if)
+ [`match with |`](conditionals.md#match-multiple-values-with)
+ [`unpacking`](conditionals.md#unpacking-a-tuple)
- [**`break, continue`**](conditionals.md#control-flow-statements): [`break`](conditionals.md#break) [`continue`](conditionals.md#continue) [`pass`](conditionals.md#going-further_2)
+ [**`control flow`**](conditionals.md#control-flow-statements):
+ [`break`](conditionals.md#break)
+ [`continue`](conditionals.md#continue)
+ [`pass`](conditionals.md#going-further_2)
- :material-repeat:{ .lg .middle } [__Loops__](loops.md)
Repeat a block of code multiple times.
- [**`for`**](loops.md#for-loops): [`enumerate`](loops.md#loop-with-index-and-value) [`loop a set number of times`](loops.md#loop-a-certain-number-of-times) [`loop through a collection`](loops.md#loop-through-a-collection) [`range`](loops.md#iterable-range) [`reversed`](loops.md#loop-in-reverse) [`zip`](loops.md#loop-with-index-and-value)
-
- [**`while`**](loops.md#while-loops): [`and`](loops.md#logical-operators) [`boolean expressions`](loops.md#boolean-expressions) [`counter and flag names`](loops.md#counter-and-flag-names) [`flag`](loops.md#using-a-flag) [`not`](loops.md#logical-operators) [`or`](loops.md#logical-operators) [`sentinel`](loops.md#sentinel)
-
- [**`break, continue`**](loops.md#control-flow-statements): [`break`](loops.md#break) [`continue`](loops.md#continue) [`else`](loops.md#else)
+ [**`for`**](loops.md#for-loops):
+ [`enumerate`](loops.md#loop-with-index-and-value)
+ [`loop a set number of times`](loops.md#loop-a-certain-number-of-times)
+ [`loop through a collection`](loops.md#loop-through-a-collection)
+ [`range`](loops.md#iterable-range)
+ [`reversed`](loops.md#loop-in-reverse)
+ [`zip`](loops.md#loop-with-index-and-value)
+
+ [**`while`**](loops.md#while-loops):
+ [`and`](loops.md#logical-operators)
+ [`boolean expressions`](loops.md#boolean-expressions)
+ [`counter and flag names`](loops.md#counter-and-flag-names)
+ [`flag`](loops.md#using-a-flag)
+ [`not`](loops.md#logical-operators)
+ [`or`](loops.md#logical-operators)
+ [`sentinel`](loops.md#sentinel)
+
+ [**`common patterns`**](loops.md#common-patterns):
+ [`accumulator`](loops.md#accumulator)
+ [`counter`](loops.md#counter)
+ [`nested loops`](loops.md#nested-loops)
+
+ [**`control flow`**](loops.md#control-flow-statements):
+ [`break`](loops.md#break)
+ [`continue`](loops.md#continue)
+ [`else`](loops.md#else)
+ [`pass`](loops.md#going-further_2)
- [**`common patterns`**](loops.md#common-patterns): [`accumulator`](loops.md#accumulator) [`counter`](loops.md#counter) [`nested loops`](loops.md#nested-loops) [`pass`](loops.md#going-further_2)
@@ -122,25 +300,55 @@ hide:
- :material-function-variant:{ .lg .middle } [__Functions__](functions.md)
- Package a block of code to run it multiple times.
+ Package a named block of code to run it at any time.
- [**`def`**](functions.md#defining-a-function): [`default parameter values`](functions.md#default-parameter-values) [`docstrings`](functions.md#docstrings) [`keyword arguments`](functions.md#keyword-arguments) [`return`](functions.md#return-values)
+ [**`def`**](functions.md#defining-a-function):
+ [`default parameter values`](functions.md#default-parameter-values)
+ [`docstrings`](functions.md#docstrings)
+ [`keep functions focused`](functions.md#keep-functions-focused)
+ [`keyword arguments`](functions.md#keyword-arguments)
+ [`return`](functions.md#return-values)
+ [`type hints`](functions.md#type-hints)
- [**`flexible arguments`**](functions.md#flexible-arguments): [`*args`](functions.md#args) [`**kwargs`](functions.md#kwargs)
+ [**`flexible arguments`**](functions.md#flexible-arguments):
+ [`**kwargs`](functions.md#kwargs)
+ [`*args`](functions.md#args)
- [**`scope`**](functions.md#scope): [`local vs global`](functions.md#local-vs-global-variables)
+ [**`scope`**](functions.md#scope):
+ [`local vs global`](functions.md#local-vs-global-variables)
[**`recursion`**](functions.md#recursion)
+ [**`decorators`**](functions.md#decorators):
+ [`arguments`](functions.md#accepting-arguments)
+ [`identity`](functions.md#advanced-uses)
+ [`original function`](functions.md#returning-the-original-function)
+ [`stacking`](functions.md#advanced-uses)
+ [`wrapping`](functions.md#wrapping-the-call)
+
- :material-package-variant:{ .lg .middle } [__Classes__](oop.md)
Bundle related values and functions to a reusable blueprint for similar objects.
- [**`class`**](oop.md#classes-and-objects): [`__init__()`](oop.md#the-__init__-method) [`object methods`](oop.md#object-methods) [`self`](oop.md#the-self-parameter)
+ [**`class`**](oop.md#classes-and-objects):
+ [`__init__()`](oop.md#the-__init__-method)
+ [`object methods`](oop.md#object-methods)
+ [`self`](oop.md#the-self-parameter)
+
+ [**`method decorators`**](oop.md#method-decorators):
+ [`@classmethod`](oop.md#classmethod)
+ [`@property`](oop.md#property)
+ [`@staticmethod`](oop.md#staticmethod)
- [**`inheritance`**](oop.md#inheritance): [`adding attributes and methods`](oop.md#adding-attributes-and-methods) [`overriding __init__()`](oop.md#overriding-__init__) [`overriding methods`](oop.md#overriding-methods) [`super()`](oop.md#using-super)
+ [**`inheritance`**](oop.md#inheritance):
+ [`adding attributes and methods`](oop.md#adding-attributes-and-methods)
+ [`overriding __init__()`](oop.md#overriding-__init__)
+ [`overriding methods`](oop.md#overriding-methods)
+ [`super()`](oop.md#using-super)
- [**`polymorphism`**](oop.md#polymorphism): [`polymorphism via inheritance`](oop.md#polymorphism-via-inheritance) [`same method name, unrelated classes`](oop.md#same-method-name-unrelated-classes)
+ [**`polymorphism`**](oop.md#polymorphism):
+ [`polymorphism via inheritance`](oop.md#polymorphism-via-inheritance)
+ [`same method name, unrelated classes`](oop.md#same-method-name-unrelated-classes)
@@ -154,9 +362,16 @@ hide:
Splitting code across files, and using someone else's code.
- [**`import`**](modules.md#importing-modules): [`as`](modules.md#as) [`from`](modules.md#from) [`import`](modules.md#import) [`import order`](modules.md#order-of-multiple-imports) [`nested paths`](modules.md#nested-paths) [`packages`](modules.md#packages)
+ [**`import`**](modules.md#importing-modules):
+ [`as`](modules.md#as)
+ [`from`](modules.md#from)
+ [`import`](modules.md#import)
+ [`import order`](modules.md#order-of-multiple-imports)
+ [`nested paths`](modules.md#nested-paths)
+ [`packages`](modules.md#packages)
- [**`your own module`**](modules.md#creating-your-own-module): [`main guard`](modules.md#the-main-guard)
+ [**`your own module`**](modules.md#creating-your-own-module):
+ [`main guard`](modules.md#the-main-guard)
[**`module, package, library`**](modules.md#modules-vs-packages-vs-libraries)
@@ -184,11 +399,21 @@ hide:
Conventions for standardized and readable Python.
- [**`PEP 8`**](style.md#pep-8-style-guide): [`blank lines`](style.md#blank-lines) [`comments`](style.md#comments) [`constants`](style.md#constants) [`docstrings`](style.md#docstrings) [`indentation`](style.md#indentation) [`naming`](style.md#naming) [`order`](style.md#file-order) [`quote style`](style.md#quote-style) [`whitespace`](style.md#whitespace)
+ [**`PEP 8`**](style.md#pep-8-style-guide):
+ [`blank lines`](style.md#blank-lines)
+ [`comments`](style.md#comments)
+ [`constants`](style.md#constants)
+ [`docstrings`](style.md#docstrings)
+ [`indentation`](style.md#indentation)
+ [`naming`](style.md#naming)
+ [`order`](style.md#file-order)
+ [`quote style`](style.md#quote-style)
+ [`whitespace`](style.md#whitespace)
- [**`Pythonic patterns`**](style.md#pythonic-patterns): [`common patterns`](style.md#common-patterns)
+ [**`Pythonic patterns`**](style.md#pythonic-patterns):
+ [`common patterns`](style.md#common-patterns)
- [**`best practices`**](style.md#additional-best-practices): [`catch exceptions`](style.md#catch-specific-exceptions) [`keep functions focused`](style.md#keep-functions-focused) [`readable print output`](style.md#readable-print-output) [`type hints`](style.md#type-hints)
+ [**`best practices`**](style.md#additional-best-practices)
[**`linter`**](style.md#linter-tool)
@@ -196,79 +421,148 @@ hide:
- :material-bug-outline:{ .lg .middle } [__Errors__](errors.md)
- How to understand, manage, and fix errors.
-
- [**`kinds of errors`**](errors.md#kinds-of-errors): [`syntax errors`](errors.md#syntax-errors) [`runtime errors`](errors.md#runtime-errors) [`logic errors`](errors.md#logic-errors)
-
- [**`fixing errors`**](errors.md#fixing-errors): [`tracebacks`](errors.md#reading-a-traceback) [`reading a syntax error message`](errors.md#reading-a-syntax-error-message) [`try, except`](errors.md#catch-with-tryexcept) [`debugging strategies`](errors.md#debugging-strategies) [`isolate the problem`](errors.md#isolate-the-problem) [`print debugging`](errors.md#print-debugging) [`rubber duck debugging`](errors.md#read-it-out-loud) [`debugger tool`](errors.md#debugger-tool) [`TODO, FIXME`](errors.md#flag-as-todofixme) [`testing`](errors.md#detect-errors-with-testing)
-
- [**`detect errors with testing`**](errors.md#detect-errors-with-testing)
+ Resolve bugs, read and utilize exceptions.
+
+ [**`kinds`**](errors.md#kinds-of-errors):
+ [`bugs`](errors.md)
+ [`exceptions`](errors.md)
+ [`logic errors`](errors.md#logic-errors)
+ [`runtime errors`](errors.md#runtime-errors)
+ [`syntax errors`](errors.md#syntax-errors)
+
+ [**`fixing`**](errors.md#fixing-errors):
+ [`debugger tool`](errors.md#debugger-tool)
+ [`debugging strategies`](errors.md#debugging-strategies)
+ [`isolate problems`](errors.md#isolate-the-problem)
+ [`print debugging`](errors.md#print-debugging)
+ [`rubber duck debugging`](errors.md#read-it-out-loud)
+ [`syntax error message`](errors.md#reading-a-syntax-error-message)
+ [`testing`](errors.md#detect-errors-with-testing)
+ [`TODO / FIXME`](errors.md#flag-as-todofixme)
+ [`tracebacks`](errors.md#reading-a-traceback)
+
+ [**`handling`**](errors.md#handling-errors):
+ [`assert`](errors.md#assert-a-condition)
+ [`else`](errors.md#finally)
+ [`finally`](errors.md#finally)
+ [`raise`](errors.md#raise-an-exception)
+ [`try/except`](errors.md#catch-with-tryexcept)
# Add-On Libraries
-
-#### Utilities { .pt-homepage-heading }
+
+#### Testing { .pt-homepage-heading }
-- :material-format-list-group:{ .lg .middle } [__collections__](libraries/collections.md) [:material-language-python:](libraries/collections.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
-
- Specialized containers with advanced functionality.
-
- [**`Counter`**](libraries/collections.md#counter): [`+ - & |`](libraries/collections.md#combine) [`counts[item]`](libraries/collections.md#count) [`elements`](libraries/collections.md#inspect) [`most_common`](libraries/collections.md#count) [`subtract`](libraries/collections.md#update) [`total`](libraries/collections.md#count) [`update`](libraries/collections.md#update)
-
- [**`defaultdict`**](libraries/collections.md#defaultdict): [`default_factory`](libraries/collections.md#defaultdict) [`get`](libraries/collections.md#reading-vs-writing)
-
- [**`namedtuple`**](libraries/collections.md#namedtuple): [`_asdict`](libraries/collections.md#convert) [`_field_defaults`](libraries/collections.md#inspect_1) [`_fields`](libraries/collections.md#inspect_1) [`_make`](libraries/collections.md#create) [`_replace`](libraries/collections.md#convert) [`defaults=`](libraries/collections.md#create)
-
- [**`deque`**](libraries/collections.md#deque): [`append`](libraries/collections.md#add) [`appendleft`](libraries/collections.md#add) [`clear`](libraries/collections.md#remove) [`copy`](libraries/collections.md#inspect_2) [`count`](libraries/collections.md#inspect_2) [`extend`](libraries/collections.md#add) [`extendleft`](libraries/collections.md#add) [`index`](libraries/collections.md#inspect_2) [`insert`](libraries/collections.md#add) [`maxlen=`](libraries/collections.md#reorder) [`pop`](libraries/collections.md#remove) [`popleft`](libraries/collections.md#remove) [`remove`](libraries/collections.md#remove) [`reverse`](libraries/collections.md#reorder) [`rotate`](libraries/collections.md#reorder)
+- :material-test-tube:{ .lg .middle } [__pytest__](libraries/pytest.md)
+[:material-download-outline:](libraries/pytest.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
- [**`OrderedDict`**](libraries/collections.md#ordereddict): [`==`](libraries/collections.md#compare) [`move_to_end`](libraries/collections.md#reorder_1) [`popitem`](libraries/collections.md#reorder_1)
-
- [**`ChainMap`**](libraries/collections.md#chainmap): [`maps`](libraries/collections.md#inspect_3) [`new_child`](libraries/collections.md#extend) [`parents`](libraries/collections.md#inspect_3)
-
- [**`User* wrapper`**](libraries/collections.md#user-wrapper-classes): [`UserDict`](libraries/collections.md#user-wrapper-classes) [`UserList`](libraries/collections.md#user-wrapper-classes) [`UserString`](libraries/collections.md#user-wrapper-classes)
-
-- :material-calendar-clock:{ .lg .middle } [__datetime__](libraries/datetime.md) [:material-language-python:](libraries/datetime.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
-
- Calculating and formatting dates and times.
-
- [`creating a specific date`](libraries/datetime.md#creating-a-specific-date) [`date`](libraries/datetime.md#creating-dates-and-times) [`strftime`](libraries/datetime.md#formatting-with-strftime)
+ Writing and running tests: assertions, fixtures, and parametrizing.
- [`difference between two dates`](libraries/datetime.md#difference-between-two-dates) [`strptime`](libraries/datetime.md#parsing-a-string-with-strptime) [`timedelta`](libraries/datetime.md#date-arithmetic)
+ [**`writing and running a test`**](libraries/pytest.md#writing-and-running-a-test):
+ [`from the command line`](libraries/pytest.md#from-the-command-line)
-- :material-dice-multiple:{ .lg .middle } [__random__](libraries/random.md) [:material-language-python:](libraries/random.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
+ [**`reading a failure`**](libraries/pytest.md#reading-a-failure)
- Random numbers, random picks, shuffled order.
+ [**`fixtures`**](libraries/pytest.md#fixtures)
- [**`randint`**](libraries/random.md#random-numbers)
+ [**`parametrizing tests`**](libraries/pytest.md#parametrizing-tests)
- [**`choice`**](libraries/random.md#random-selections): [`sample`](libraries/random.md#sampling-without-replacement) [`shuffle`](libraries/random.md#shuffling-a-list)
+ [**`testing for exceptions`**](libraries/pytest.md#testing-for-exceptions)
-
-#### Testing { .pt-homepage-heading }
+
+#### Utilities { .pt-homepage-heading }
-- :material-test-tube:{ .lg .middle } [__pytest__](libraries/pytest.md) [:material-download-outline:](libraries/pytest.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-format-list-group:{ .lg .middle } [__collections__](libraries/collections.md)
+[:material-language-python:](libraries/collections.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
- Writing and running tests: assertions, fixtures, and parametrizing.
+ Specialized containers with advanced functionality.
- [**`writing and running a test`**](libraries/pytest.md#writing-and-running-a-test): [`from the command line`](libraries/pytest.md#from-the-command-line)
+ [**`Counter`**](libraries/collections.md#counter):
+ [`+ - & |`](libraries/collections.md#combine)
+ [`counts[item]`](libraries/collections.md#count)
+ [`elements`](libraries/collections.md#inspect)
+ [`most_common`](libraries/collections.md#count)
+ [`subtract`](libraries/collections.md#update)
+ [`total`](libraries/collections.md#count)
+ [`update`](libraries/collections.md#update)
+
+ [**`defaultdict`**](libraries/collections.md#defaultdict):
+ [`default_factory`](libraries/collections.md#defaultdict)
+ [`get`](libraries/collections.md#reading-vs-writing)
+
+ [**`namedtuple`**](libraries/collections.md#namedtuple):
+ [`_asdict`](libraries/collections.md#convert)
+ [`_field_defaults`](libraries/collections.md#inspect_1)
+ [`_fields`](libraries/collections.md#inspect_1)
+ [`_make`](libraries/collections.md#create)
+ [`_replace`](libraries/collections.md#convert)
+ [`defaults=`](libraries/collections.md#create)
+
+ [**`deque`**](libraries/collections.md#deque):
+ [`append`](libraries/collections.md#add)
+ [`appendleft`](libraries/collections.md#add)
+ [`clear`](libraries/collections.md#remove)
+ [`copy`](libraries/collections.md#inspect_2)
+ [`count`](libraries/collections.md#inspect_2)
+ [`extend`](libraries/collections.md#add)
+ [`extendleft`](libraries/collections.md#add)
+ [`index`](libraries/collections.md#inspect_2)
+ [`insert`](libraries/collections.md#add)
+ [`maxlen=`](libraries/collections.md#reorder)
+ [`pop`](libraries/collections.md#remove)
+ [`popleft`](libraries/collections.md#remove)
+ [`remove`](libraries/collections.md#remove)
+ [`reverse`](libraries/collections.md#reorder)
+ [`rotate`](libraries/collections.md#reorder)
+
+ [**`OrderedDict`**](libraries/collections.md#ordereddict):
+ [`==`](libraries/collections.md#compare)
+ [`move_to_end`](libraries/collections.md#reorder_1)
+ [`popitem`](libraries/collections.md#reorder_1)
+
+ [**`ChainMap`**](libraries/collections.md#chainmap):
+ [`maps`](libraries/collections.md#inspect_3)
+ [`new_child`](libraries/collections.md#extend)
+ [`parents`](libraries/collections.md#inspect_3)
+
+ [**`User* wrapper`**](libraries/collections.md#user-wrapper-classes):
+ [`UserDict`](libraries/collections.md#user-wrapper-classes)
+ [`UserList`](libraries/collections.md#user-wrapper-classes)
+ [`UserString`](libraries/collections.md#user-wrapper-classes)
+
+- :material-calendar-clock:{ .lg .middle } [__datetime__](libraries/datetime.md)
+[:material-language-python:](libraries/datetime.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
- [**`reading a failure`**](libraries/pytest.md#reading-a-failure)
+ Calculating and formatting dates and times.
- [**`fixtures`**](libraries/pytest.md#fixtures)
+ [`creating a specific date`](libraries/datetime.md#creating-a-specific-date)
+ [`date`](libraries/datetime.md#creating-dates-and-times)
+ [`strftime`](libraries/datetime.md#formatting-with-strftime)
- [**`parametrizing tests`**](libraries/pytest.md#parametrizing-tests)
+ [`difference between two dates`](libraries/datetime.md#difference-between-two-dates)
+ [`strptime`](libraries/datetime.md#parsing-a-string-with-strptime)
+ [`timedelta`](libraries/datetime.md#date-arithmetic)
- [**`testing for exceptions`**](libraries/pytest.md#testing-for-exceptions)
+- :material-dice-multiple:{ .lg .middle } [__random__](libraries/random.md)
+[:material-language-python:](libraries/random.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
+
+ Random numbers, random picks, shuffled order.
+
+ [**`randint`**](libraries/random.md#random-numbers)
+
+ [**`choice`**](libraries/random.md#random-selections):
+ [`sample`](libraries/random.md#sampling-without-replacement)
+ [`shuffle`](libraries/random.md#shuffling-a-list)
@@ -278,19 +572,24 @@ hide:
-- :material-file-delimited-outline:{ .lg .middle } [__csv__](libraries/csv.md) [:material-language-python:](libraries/csv.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
+- :material-file-delimited-outline:{ .lg .middle } [__csv__](libraries/csv.md)
+[:material-language-python:](libraries/csv.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
Reading and writing spreadsheets.
[`writer`](libraries/csv.md#writing-csv-files)
- [`DictReader`](libraries/csv.md#reading-rows-as-dictionaries) [`reader`](libraries/csv.md#reading-csv-files)
+ [`DictReader`](libraries/csv.md#reading-rows-as-dictionaries)
+ [`reader`](libraries/csv.md#reading-csv-files)
-- :material-chart-line:{ .lg .middle } [__matplotlib__](libraries/matplotlib.md) [:material-download-outline:](libraries/matplotlib.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-chart-line:{ .lg .middle } [__matplotlib__](libraries/matplotlib.md)
+[:material-download-outline:](libraries/matplotlib.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
Charts and plots: line, bar, and scatter, built directly from plain Python data.
- [**`line plots`**](libraries/matplotlib.md#line-plots): [`labels and title`](libraries/matplotlib.md#labels-and-title) [`multiple lines and a legend`](libraries/matplotlib.md#multiple-lines-and-a-legend)
+ [**`line plots`**](libraries/matplotlib.md#line-plots):
+ [`labels and title`](libraries/matplotlib.md#labels-and-title)
+ [`multiple lines and a legend`](libraries/matplotlib.md#multiple-lines-and-a-legend)
[**`bar charts`**](libraries/matplotlib.md#bar-charts)
@@ -300,21 +599,28 @@ hide:
[**`saving a figure`**](libraries/matplotlib.md#saving-a-figure)
-- :material-matrix:{ .lg .middle } [__NumPy__](libraries/numpy.md) [:material-download-outline:](libraries/numpy.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-matrix:{ .lg .middle } [__NumPy__](libraries/numpy.md)
+[:material-download-outline:](libraries/numpy.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
Fast numeric arrays, with math applied to a whole array at once instead of item by item.
- [**`array operations`**](libraries/numpy.md#array-operations): [`boolean mask`](libraries/numpy.md#filtering-with-a-boolean-mask) [`mean`](libraries/numpy.md#aggregating-an-array)
+ [**`array operations`**](libraries/numpy.md#array-operations):
+ [`boolean mask`](libraries/numpy.md#filtering-with-a-boolean-mask)
+ [`mean`](libraries/numpy.md#aggregating-an-array)
- [`arange`](libraries/numpy.md#building-arrays-without-a-list) [`ndarray`](libraries/numpy.md#creating-arrays)
+ [`arange`](libraries/numpy.md#building-arrays-without-a-list)
+ [`ndarray`](libraries/numpy.md#creating-arrays)
-- :material-table:{ .lg .middle } [__pandas__](libraries/pandas.md) [:material-download-outline:](libraries/pandas.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-table:{ .lg .middle } [__pandas__](libraries/pandas.md)
+[:material-download-outline:](libraries/pandas.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
Tabular data: rows and columns, like a spreadsheet, built on top of NumPy.
[**`DataFrame`**](libraries/pandas.md#building-a-dataframe)
- [**`working with a DataFrame`**](libraries/pandas.md#working-with-a-dataframe): [`mean`](libraries/pandas.md#summarizing-a-column) [`sort_values`](libraries/pandas.md#sorting-rows)
+ [**`working with a DataFrame`**](libraries/pandas.md#working-with-a-dataframe):
+ [`mean`](libraries/pandas.md#summarizing-a-column)
+ [`sort_values`](libraries/pandas.md#sorting-rows)
@@ -324,21 +630,27 @@ hide:
-- :material-code-json:{ .lg .middle } [__json__](libraries/json.md) [:material-language-python:](libraries/json.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
+- :material-code-json:{ .lg .middle } [__json__](libraries/json.md)
+[:material-language-python:](libraries/json.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
Reading and writing JSON data: nested dicts and lists, saved to a file or a string.
[`dump`](libraries/json.md#writing-json-files)
- [`load`](libraries/json.md#reading-json-files) [`nested data`](libraries/json.md#nested-data)
+ [`load`](libraries/json.md#reading-json-files)
+ [`nested data`](libraries/json.md#nested-data)
[`loads`](libraries/json.md#working-with-strings-instead-of-files)
-- :material-webhook:{ .lg .middle } [__requests__](libraries/requests.md) [:material-download-outline:](libraries/requests.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-webhook:{ .lg .middle } [__requests__](libraries/requests.md)
+[:material-download-outline:](libraries/requests.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
Fetching data over the internet, like asking a website or API for information.
- [**`get`**](libraries/requests.md#making-a-request): [`json`](libraries/requests.md#parsing-json) [`params`](libraries/requests.md#query-parameters) [`status_code`](libraries/requests.md#checking-the-status-code)
+ [**`get`**](libraries/requests.md#making-a-request):
+ [`json`](libraries/requests.md#parsing-json)
+ [`params`](libraries/requests.md#query-parameters)
+ [`status_code`](libraries/requests.md#checking-the-status-code)
[**`error handling`**](libraries/requests.md#handling-request-errors)
@@ -350,33 +662,50 @@ hide:
-- :material-image-outline:{ .lg .middle } [__Pillow__](libraries/pillow.md) [:material-download-outline:](libraries/pillow.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-image-outline:{ .lg .middle } [__Pillow__](libraries/pillow.md)
+[:material-download-outline:](libraries/pillow.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
Opening, editing, and saving images, built around one Image object.
[**`why Pillow?`**](libraries/pillow.md#why-pillow)
- [**`Image`**](libraries/pillow.md#the-image): [`basic operations`](libraries/pillow.md#basic-operations) [`crop`](libraries/pillow.md#crop) [`image modes`](libraries/pillow.md#image-modes) [`opening and saving images`](libraries/pillow.md#opening-and-saving-images) [`resize`](libraries/pillow.md#resize) [`rotate and flip`](libraries/pillow.md#rotate-and-flip)
+ [**`Image`**](libraries/pillow.md#the-image):
+ [`basic operations`](libraries/pillow.md#basic-operations)
+ [`crop`](libraries/pillow.md#crop)
+ [`image modes`](libraries/pillow.md#image-modes)
+ [`opening and saving images`](libraries/pillow.md#opening-and-saving-images)
+ [`resize`](libraries/pillow.md#resize)
+ [`rotate and flip`](libraries/pillow.md#rotate-and-flip)
- [**`ImageOps`**](libraries/pillow.md#imageops-module): [`common ImageOps functions`](libraries/pillow.md#common-imageops-functions)
+ [**`ImageOps`**](libraries/pillow.md#imageops-module):
+ [`common ImageOps functions`](libraries/pillow.md#common-imageops-functions)
- [**`ImageDraw`**](libraries/pillow.md#imagedraw-module): [`shapes and lines`](libraries/pillow.md#shapes-and-lines)
+ [**`ImageDraw`**](libraries/pillow.md#imagedraw-module):
+ [`shapes and lines`](libraries/pillow.md#shapes-and-lines)
- [**`ImageFont`**](libraries/pillow.md#imagefont-module): [`loading a font`](libraries/pillow.md#loading-a-font)
+ [**`ImageFont`**](libraries/pillow.md#imagefont-module):
+ [`loading a font`](libraries/pillow.md#loading-a-font)
- [**`ImageColor`**](libraries/pillow.md#imagecolor-module): [`converting color names`](libraries/pillow.md#converting-color-names)
+ [**`ImageColor`**](libraries/pillow.md#imagecolor-module):
+ [`converting color names`](libraries/pillow.md#converting-color-names)
- [**`ImageFilter`**](libraries/pillow.md#imagefilter-module): [`applying a filter`](libraries/pillow.md#applying-a-filter)
+ [**`ImageFilter`**](libraries/pillow.md#imagefilter-module):
+ [`applying a filter`](libraries/pillow.md#applying-a-filter)
- [**`ImageEnhance`**](libraries/pillow.md#imageenhance-module): [`enhancing an image`](libraries/pillow.md#enhancing-an-image)
+ [**`ImageEnhance`**](libraries/pillow.md#imageenhance-module):
+ [`enhancing an image`](libraries/pillow.md#enhancing-an-image)
- [**`ImageChops`**](libraries/pillow.md#imagechops-module): [`comparing and combining images`](libraries/pillow.md#comparing-and-combining-images)
+ [**`ImageChops`**](libraries/pillow.md#imagechops-module):
+ [`comparing and combining images`](libraries/pillow.md#comparing-and-combining-images)
- [**`convert`**](libraries/pillow.md#format-conversion): [`converting between formats`](libraries/pillow.md#converting-between-formats)
+ [**`convert`**](libraries/pillow.md#format-conversion):
+ [`converting between formats`](libraries/pillow.md#converting-between-formats)
- [**`ImageSequence`**](libraries/pillow.md#imagesequence-module): [`looping over GIF frames`](libraries/pillow.md#looping-over-gif-frames)
+ [**`ImageSequence`**](libraries/pillow.md#imagesequence-module):
+ [`looping over GIF frames`](libraries/pillow.md#looping-over-gif-frames)
- [**`putting it together`**](libraries/pillow.md#putting-it-together): [`an interactive filter tool`](libraries/pillow.md#an-interactive-filter-tool)
+ [**`putting it together`**](libraries/pillow.md#putting-it-together):
+ [`an interactive filter tool`](libraries/pillow.md#an-interactive-filter-tool)
@@ -386,27 +715,41 @@ hide:
-- :material-application-outline:{ .lg .middle } [__Tkinter__](libraries/tkinter.md) [:material-language-python:](libraries/tkinter.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
+- :material-application-outline:{ .lg .middle } [__Tkinter__](libraries/tkinter.md)
+[:material-language-python:](libraries/tkinter.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
Creating desktop applications: text, buttons, dropdowns, forms, output, etc.
[**`Tk`**](libraries/tkinter.md#creating-a-window)
- [**`Button`**](libraries/tkinter.md#widgets): [`Button`](libraries/tkinter.md#button) [`Entry`](libraries/tkinter.md#entry) [`Label`](libraries/tkinter.md#label)
+ [**`Button`**](libraries/tkinter.md#widgets):
+ [`Button`](libraries/tkinter.md#button)
+ [`Entry`](libraries/tkinter.md#entry)
+ [`Label`](libraries/tkinter.md#label)
- [**`pack`**](libraries/tkinter.md#layout-managers): [`grid`](libraries/tkinter.md#grid) [`pack`](libraries/tkinter.md#pack)
+ [**`pack`**](libraries/tkinter.md#layout-managers):
+ [`grid`](libraries/tkinter.md#grid)
+ [`pack`](libraries/tkinter.md#pack)
- [**`configure`**](libraries/tkinter.md#configuring-widgets): [`reading and changing options`](libraries/tkinter.md#reading-and-changing-options)
+ [**`configure`**](libraries/tkinter.md#configuring-widgets):
+ [`reading and changing options`](libraries/tkinter.md#reading-and-changing-options)
- [**`command`**](libraries/tkinter.md#handling-events): [`binding events`](libraries/tkinter.md#binding-events) [`command callbacks`](libraries/tkinter.md#command-callbacks)
+ [**`command`**](libraries/tkinter.md#handling-events):
+ [`binding events`](libraries/tkinter.md#binding-events)
+ [`command callbacks`](libraries/tkinter.md#command-callbacks)
- [**`Style`**](libraries/tkinter.md#styling-with-ttk): [`customizing a style`](libraries/tkinter.md#customizing-a-style)
+ [**`Style`**](libraries/tkinter.md#styling-with-ttk):
+ [`customizing a style`](libraries/tkinter.md#customizing-a-style)
- [**`messagebox`**](libraries/tkinter.md#dialogs): [`file dialogs`](libraries/tkinter.md#file-dialogs) [`message boxes`](libraries/tkinter.md#message-boxes)
+ [**`messagebox`**](libraries/tkinter.md#dialogs):
+ [`file dialogs`](libraries/tkinter.md#file-dialogs)
+ [`message boxes`](libraries/tkinter.md#message-boxes)
- [**`winfo_width`**](libraries/tkinter.md#introspecting-widgets): [`winfo methods`](libraries/tkinter.md#winfo-methods)
+ [**`winfo_width`**](libraries/tkinter.md#introspecting-widgets):
+ [`winfo methods`](libraries/tkinter.md#winfo-methods)
- [**`putting it together`**](libraries/tkinter.md#putting-it-together): [`a simple form`](libraries/tkinter.md#a-simple-form)
+ [**`putting it together`**](libraries/tkinter.md#putting-it-together):
+ [`a simple form`](libraries/tkinter.md#a-simple-form)
@@ -416,27 +759,43 @@ hide:
-- :material-face-recognition:{ .lg .middle } [__OpenCV__](libraries/opencv.md) [:material-download-outline:](libraries/opencv.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-face-recognition:{ .lg .middle } [__OpenCV__](libraries/opencv.md)
+[:material-download-outline:](libraries/opencv.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
Real-time image and video analysis, built directly on NumPy arrays: color spaces, edge detection, face detection.
- [**`reading, displaying, saving images`**](libraries/opencv.md#reading-displaying-and-saving-images): [`displaying a window`](libraries/opencv.md#displaying-a-window) [`imread`](libraries/opencv.md#reading-a-file) [`saving a file`](libraries/opencv.md#saving-a-file)
+ [**`reading, displaying, saving images`**](libraries/opencv.md#reading-displaying-and-saving-images):
+ [`displaying a window`](libraries/opencv.md#displaying-a-window)
+ [`imread`](libraries/opencv.md#reading-a-file)
+ [`saving a file`](libraries/opencv.md#saving-a-file)
- [**`drawing`**](libraries/opencv.md#drawing-shapes-and-text): [`shapes and lines`](libraries/opencv.md#shapes-and-lines) [`text`](libraries/opencv.md#text)
+ [**`drawing`**](libraries/opencv.md#drawing-shapes-and-text):
+ [`shapes and lines`](libraries/opencv.md#shapes-and-lines)
+ [`text`](libraries/opencv.md#text)
- [**`color spaces`**](libraries/opencv.md#color-spaces): [`cvtColor`](libraries/opencv.md#converting-color-spaces)
+ [**`color spaces`**](libraries/opencv.md#color-spaces):
+ [`cvtColor`](libraries/opencv.md#converting-color-spaces)
- [**`CascadeClassifier`**](libraries/opencv.md#face-detection-with-cascade-classifiers): [`detecting and labeling faces`](libraries/opencv.md#detecting-and-labeling-faces)
+ [**`CascadeClassifier`**](libraries/opencv.md#face-detection-with-cascade-classifiers):
+ [`detecting and labeling faces`](libraries/opencv.md#detecting-and-labeling-faces)
- [**`VideoCapture`**](libraries/opencv.md#working-with-video): [`reading frames`](libraries/opencv.md#reading-frames)
+ [**`VideoCapture`**](libraries/opencv.md#working-with-video):
+ [`reading frames`](libraries/opencv.md#reading-frames)
- [**`basic operations`**](libraries/opencv.md#basic-operations): [`cropping`](libraries/opencv.md#cropping) [`resize`](libraries/opencv.md#resize) [`rotating`](libraries/opencv.md#rotating)
+ [**`basic operations`**](libraries/opencv.md#basic-operations):
+ [`cropping`](libraries/opencv.md#cropping)
+ [`resize`](libraries/opencv.md#resize)
+ [`rotating`](libraries/opencv.md#rotating)
- [**`thresholding, edge detection`**](libraries/opencv.md#thresholding-and-edge-detection): [`Canny`](libraries/opencv.md#edge-detection) [`threshold`](libraries/opencv.md#threshold)
+ [**`thresholding, edge detection`**](libraries/opencv.md#thresholding-and-edge-detection):
+ [`Canny`](libraries/opencv.md#edge-detection)
+ [`threshold`](libraries/opencv.md#threshold)
- [**`blurring`**](libraries/opencv.md#blurring): [`gaussian blur`](libraries/opencv.md#gaussian-blur)
+ [**`blurring`**](libraries/opencv.md#blurring):
+ [`gaussian blur`](libraries/opencv.md#gaussian-blur)
- [**`contours`**](libraries/opencv.md#contours): [`finding and drawing contours`](libraries/opencv.md#finding-and-drawing-contours)
+ [**`contours`**](libraries/opencv.md#contours):
+ [`finding and drawing contours`](libraries/opencv.md#finding-and-drawing-contours)
diff --git a/docs/libraries/index.md b/docs/libraries/index.md
index 5429db9..3bbb279 100644
--- a/docs/libraries/index.md
+++ b/docs/libraries/index.md
@@ -11,66 +11,116 @@ Libraries allow us to apply Python to real tasks. These are a few popular ones,
-
-#### Utilities { .pt-homepage-heading }
+
+#### Testing { .pt-homepage-heading }
-- :material-format-list-group:{ .lg .middle } [__collections__](collections.md) [:material-language-python:](collections.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
-
- Specialized containers: counting items, grouping with defaults, named tuples, fast queues.
-
- [**`Counter`**](collections.md#counter): [`+ - & |`](collections.md#combine) [`counts[item]`](collections.md#count) [`elements`](collections.md#inspect) [`most_common`](collections.md#count) [`subtract`](collections.md#update) [`total`](collections.md#count) [`update`](collections.md#update)
-
- [**`defaultdict`**](collections.md#defaultdict): [`default_factory`](collections.md#defaultdict) [`get`](collections.md#reading-vs-writing)
-
- [**`namedtuple`**](collections.md#namedtuple): [`_asdict`](collections.md#convert) [`_field_defaults`](collections.md#inspect_1) [`_fields`](collections.md#inspect_1) [`_make`](collections.md#create) [`_replace`](collections.md#convert) [`defaults=`](collections.md#create)
-
- [**`deque`**](collections.md#deque): [`append`](collections.md#add) [`appendleft`](collections.md#add) [`clear`](collections.md#remove) [`copy`](collections.md#inspect_2) [`count`](collections.md#inspect_2) [`extend`](collections.md#add) [`extendleft`](collections.md#add) [`index`](collections.md#inspect_2) [`insert`](collections.md#add) [`maxlen=`](collections.md#reorder) [`pop`](collections.md#remove) [`popleft`](collections.md#remove) [`remove`](collections.md#remove) [`reverse`](collections.md#reorder) [`rotate`](collections.md#reorder)
+- :material-test-tube:{ .lg .middle } [__pytest__](pytest.md)
+[:material-download-outline:](pytest.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
- [**`OrderedDict`**](collections.md#ordereddict): [`==`](collections.md#compare) [`move_to_end`](collections.md#reorder_1) [`popitem`](collections.md#reorder_1)
-
- [**`ChainMap`**](collections.md#chainmap): [`maps`](collections.md#inspect_3) [`new_child`](collections.md#extend) [`parents`](collections.md#inspect_3)
-
- [**`User* wrapper`**](collections.md#user-wrapper-classes): [`UserDict`](collections.md#user-wrapper-classes) [`UserList`](collections.md#user-wrapper-classes) [`UserString`](collections.md#user-wrapper-classes)
-
-- :material-calendar-clock:{ .lg .middle } [__datetime__](datetime.md) [:material-language-python:](datetime.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
-
- Calculating and formatting dates and times.
-
- [`creating a specific date`](datetime.md#creating-a-specific-date) [`date`](datetime.md#creating-dates-and-times) [`strftime`](datetime.md#formatting-with-strftime)
+ Writing and running tests: assertions, fixtures, and parametrizing.
- [`difference between two dates`](datetime.md#difference-between-two-dates) [`strptime`](datetime.md#parsing-a-string-with-strptime) [`timedelta`](datetime.md#date-arithmetic)
+ [**`writing and running a test`**](pytest.md#writing-and-running-a-test):
+ [`from the command line`](pytest.md#from-the-command-line)
-- :material-dice-multiple:{ .lg .middle } [__random__](random.md) [:material-language-python:](random.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
+ [**`reading a failure`**](pytest.md#reading-a-failure)
- Random numbers, random picks, shuffled order.
+ [**`fixtures`**](pytest.md#fixtures)
- [**`randint`**](random.md#random-numbers)
+ [**`parametrizing tests`**](pytest.md#parametrizing-tests)
- [**`choice`**](random.md#random-selections): [`sample`](random.md#sampling-without-replacement) [`shuffle`](random.md#shuffling-a-list)
+ [**`testing for exceptions`**](pytest.md#testing-for-exceptions)
-
-#### Testing { .pt-homepage-heading }
+
+#### Utilities { .pt-homepage-heading }
-- :material-test-tube:{ .lg .middle } [__pytest__](pytest.md) [:material-download-outline:](pytest.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-format-list-group:{ .lg .middle } [__collections__](collections.md)
+[:material-language-python:](collections.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
- Writing and running tests: assertions, fixtures, and parametrizing.
+ Specialized containers: counting items, grouping with defaults, named tuples, fast queues.
- [**`writing and running a test`**](pytest.md#writing-and-running-a-test): [`from the command line`](pytest.md#from-the-command-line)
+ [**`Counter`**](collections.md#counter):
+ [`+ - & |`](collections.md#combine)
+ [`counts[item]`](collections.md#count)
+ [`elements`](collections.md#inspect)
+ [`most_common`](collections.md#count)
+ [`subtract`](collections.md#update)
+ [`total`](collections.md#count)
+ [`update`](collections.md#update)
+
+ [**`defaultdict`**](collections.md#defaultdict):
+ [`default_factory`](collections.md#defaultdict)
+ [`get`](collections.md#reading-vs-writing)
+
+ [**`namedtuple`**](collections.md#namedtuple):
+ [`_asdict`](collections.md#convert)
+ [`_field_defaults`](collections.md#inspect_1)
+ [`_fields`](collections.md#inspect_1)
+ [`_make`](collections.md#create)
+ [`_replace`](collections.md#convert)
+ [`defaults=`](collections.md#create)
+
+ [**`deque`**](collections.md#deque):
+ [`append`](collections.md#add)
+ [`appendleft`](collections.md#add)
+ [`clear`](collections.md#remove)
+ [`copy`](collections.md#inspect_2)
+ [`count`](collections.md#inspect_2)
+ [`extend`](collections.md#add)
+ [`extendleft`](collections.md#add)
+ [`index`](collections.md#inspect_2)
+ [`insert`](collections.md#add)
+ [`maxlen=`](collections.md#reorder)
+ [`pop`](collections.md#remove)
+ [`popleft`](collections.md#remove)
+ [`remove`](collections.md#remove)
+ [`reverse`](collections.md#reorder)
+ [`rotate`](collections.md#reorder)
+
+ [**`OrderedDict`**](collections.md#ordereddict):
+ [`==`](collections.md#compare)
+ [`move_to_end`](collections.md#reorder_1)
+ [`popitem`](collections.md#reorder_1)
+
+ [**`ChainMap`**](collections.md#chainmap):
+ [`maps`](collections.md#inspect_3)
+ [`new_child`](collections.md#extend)
+ [`parents`](collections.md#inspect_3)
+
+ [**`User* wrapper`**](collections.md#user-wrapper-classes):
+ [`UserDict`](collections.md#user-wrapper-classes)
+ [`UserList`](collections.md#user-wrapper-classes)
+ [`UserString`](collections.md#user-wrapper-classes)
+
+- :material-calendar-clock:{ .lg .middle } [__datetime__](datetime.md)
+[:material-language-python:](datetime.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
- [**`reading a failure`**](pytest.md#reading-a-failure)
+ Calculating and formatting dates and times.
- [**`fixtures`**](pytest.md#fixtures)
+ [`creating a specific date`](datetime.md#creating-a-specific-date)
+ [`date`](datetime.md#creating-dates-and-times)
+ [`strftime`](datetime.md#formatting-with-strftime)
- [**`parametrizing tests`**](pytest.md#parametrizing-tests)
+ [`difference between two dates`](datetime.md#difference-between-two-dates)
+ [`strptime`](datetime.md#parsing-a-string-with-strptime)
+ [`timedelta`](datetime.md#date-arithmetic)
- [**`testing for exceptions`**](pytest.md#testing-for-exceptions)
+- :material-dice-multiple:{ .lg .middle } [__random__](random.md)
+[:material-language-python:](random.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
+
+ Random numbers, random picks, shuffled order.
+
+ [**`randint`**](random.md#random-numbers)
+
+ [**`choice`**](random.md#random-selections):
+ [`sample`](random.md#sampling-without-replacement)
+ [`shuffle`](random.md#shuffling-a-list)
@@ -80,19 +130,24 @@ Libraries allow us to apply Python to real tasks. These are a few popular ones,
-- :material-file-delimited-outline:{ .lg .middle } [__csv__](csv.md) [:material-language-python:](csv.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
+- :material-file-delimited-outline:{ .lg .middle } [__csv__](csv.md)
+[:material-language-python:](csv.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
Reading and writing spreadsheets.
[`writer`](csv.md#writing-csv-files)
- [`DictReader`](csv.md#reading-rows-as-dictionaries) [`reader`](csv.md#reading-csv-files)
+ [`DictReader`](csv.md#reading-rows-as-dictionaries)
+ [`reader`](csv.md#reading-csv-files)
-- :material-chart-line:{ .lg .middle } [__matplotlib__](matplotlib.md) [:material-download-outline:](matplotlib.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-chart-line:{ .lg .middle } [__matplotlib__](matplotlib.md)
+[:material-download-outline:](matplotlib.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
Charts and plots: line, bar, and scatter, built directly from plain Python data.
- [**`line plots`**](matplotlib.md#line-plots): [`labels and title`](matplotlib.md#labels-and-title) [`multiple lines and a legend`](matplotlib.md#multiple-lines-and-a-legend)
+ [**`line plots`**](matplotlib.md#line-plots):
+ [`labels and title`](matplotlib.md#labels-and-title)
+ [`multiple lines and a legend`](matplotlib.md#multiple-lines-and-a-legend)
[**`bar charts`**](matplotlib.md#bar-charts)
@@ -102,21 +157,28 @@ Libraries allow us to apply Python to real tasks. These are a few popular ones,
[**`saving a figure`**](matplotlib.md#saving-a-figure)
-- :material-matrix:{ .lg .middle } [__NumPy__](numpy.md) [:material-download-outline:](numpy.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-matrix:{ .lg .middle } [__NumPy__](numpy.md)
+[:material-download-outline:](numpy.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
Fast numeric arrays, with math applied to a whole array at once instead of item by item.
- [**`array operations`**](numpy.md#array-operations): [`boolean mask`](numpy.md#filtering-with-a-boolean-mask) [`mean`](numpy.md#aggregating-an-array)
+ [**`array operations`**](numpy.md#array-operations):
+ [`boolean mask`](numpy.md#filtering-with-a-boolean-mask)
+ [`mean`](numpy.md#aggregating-an-array)
- [`arange`](numpy.md#building-arrays-without-a-list) [`ndarray`](numpy.md#creating-arrays)
+ [`arange`](numpy.md#building-arrays-without-a-list)
+ [`ndarray`](numpy.md#creating-arrays)
-- :material-table:{ .lg .middle } [__pandas__](pandas.md) [:material-download-outline:](pandas.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-table:{ .lg .middle } [__pandas__](pandas.md)
+[:material-download-outline:](pandas.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
Tabular data: rows and columns, like a spreadsheet, built on top of NumPy.
[**`DataFrame`**](pandas.md#building-a-dataframe)
- [**`working with a DataFrame`**](pandas.md#working-with-a-dataframe): [`mean`](pandas.md#summarizing-a-column) [`sort_values`](pandas.md#sorting-rows)
+ [**`working with a DataFrame`**](pandas.md#working-with-a-dataframe):
+ [`mean`](pandas.md#summarizing-a-column)
+ [`sort_values`](pandas.md#sorting-rows)
@@ -126,21 +188,27 @@ Libraries allow us to apply Python to real tasks. These are a few popular ones,
-- :material-code-json:{ .lg .middle } [__json__](json.md) [:material-language-python:](json.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
+- :material-code-json:{ .lg .middle } [__json__](json.md)
+[:material-language-python:](json.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
Reading and writing JSON data: nested dicts and lists, saved to a file or a string.
[`dump`](json.md#writing-json-files)
- [`load`](json.md#reading-json-files) [`nested data`](json.md#nested-data)
+ [`load`](json.md#reading-json-files)
+ [`nested data`](json.md#nested-data)
[`loads`](json.md#working-with-strings-instead-of-files)
-- :material-webhook:{ .lg .middle } [__requests__](requests.md) [:material-download-outline:](requests.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-webhook:{ .lg .middle } [__requests__](requests.md)
+[:material-download-outline:](requests.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
Fetching data over the internet, like asking a website or API for information.
- [**`get`**](requests.md#making-a-request): [`json`](requests.md#parsing-json) [`params`](requests.md#query-parameters) [`status_code`](requests.md#checking-the-status-code)
+ [**`get`**](requests.md#making-a-request):
+ [`json`](requests.md#parsing-json)
+ [`params`](requests.md#query-parameters)
+ [`status_code`](requests.md#checking-the-status-code)
[**`error handling`**](requests.md#handling-request-errors)
@@ -152,33 +220,50 @@ Libraries allow us to apply Python to real tasks. These are a few popular ones,
-- :material-image-outline:{ .lg .middle } [__Pillow__](pillow.md) [:material-download-outline:](pillow.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-image-outline:{ .lg .middle } [__Pillow__](pillow.md)
+[:material-download-outline:](pillow.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
Opening, editing, and saving images, built around one Image object.
[**`why Pillow?`**](pillow.md#why-pillow)
- [**`Image`**](pillow.md#the-image): [`basic operations`](pillow.md#basic-operations) [`crop`](pillow.md#crop) [`image modes`](pillow.md#image-modes) [`opening and saving images`](pillow.md#opening-and-saving-images) [`resize`](pillow.md#resize) [`rotate and flip`](pillow.md#rotate-and-flip)
+ [**`Image`**](pillow.md#the-image):
+ [`basic operations`](pillow.md#basic-operations)
+ [`crop`](pillow.md#crop)
+ [`image modes`](pillow.md#image-modes)
+ [`opening and saving images`](pillow.md#opening-and-saving-images)
+ [`resize`](pillow.md#resize)
+ [`rotate and flip`](pillow.md#rotate-and-flip)
- [**`ImageOps`**](pillow.md#imageops-module): [`common ImageOps functions`](pillow.md#common-imageops-functions)
+ [**`ImageOps`**](pillow.md#imageops-module):
+ [`common ImageOps functions`](pillow.md#common-imageops-functions)
- [**`ImageDraw`**](pillow.md#imagedraw-module): [`shapes and lines`](pillow.md#shapes-and-lines)
+ [**`ImageDraw`**](pillow.md#imagedraw-module):
+ [`shapes and lines`](pillow.md#shapes-and-lines)
- [**`ImageFont`**](pillow.md#imagefont-module): [`loading a font`](pillow.md#loading-a-font)
+ [**`ImageFont`**](pillow.md#imagefont-module):
+ [`loading a font`](pillow.md#loading-a-font)
- [**`ImageColor`**](pillow.md#imagecolor-module): [`converting color names`](pillow.md#converting-color-names)
+ [**`ImageColor`**](pillow.md#imagecolor-module):
+ [`converting color names`](pillow.md#converting-color-names)
- [**`ImageFilter`**](pillow.md#imagefilter-module): [`applying a filter`](pillow.md#applying-a-filter)
+ [**`ImageFilter`**](pillow.md#imagefilter-module):
+ [`applying a filter`](pillow.md#applying-a-filter)
- [**`ImageEnhance`**](pillow.md#imageenhance-module): [`enhancing an image`](pillow.md#enhancing-an-image)
+ [**`ImageEnhance`**](pillow.md#imageenhance-module):
+ [`enhancing an image`](pillow.md#enhancing-an-image)
- [**`ImageChops`**](pillow.md#imagechops-module): [`comparing and combining images`](pillow.md#comparing-and-combining-images)
+ [**`ImageChops`**](pillow.md#imagechops-module):
+ [`comparing and combining images`](pillow.md#comparing-and-combining-images)
- [**`convert`**](pillow.md#format-conversion): [`converting between formats`](pillow.md#converting-between-formats)
+ [**`convert`**](pillow.md#format-conversion):
+ [`converting between formats`](pillow.md#converting-between-formats)
- [**`ImageSequence`**](pillow.md#imagesequence-module): [`looping over GIF frames`](pillow.md#looping-over-gif-frames)
+ [**`ImageSequence`**](pillow.md#imagesequence-module):
+ [`looping over GIF frames`](pillow.md#looping-over-gif-frames)
- [**`putting it together`**](pillow.md#putting-it-together): [`an interactive filter tool`](pillow.md#an-interactive-filter-tool)
+ [**`putting it together`**](pillow.md#putting-it-together):
+ [`an interactive filter tool`](pillow.md#an-interactive-filter-tool)
@@ -188,27 +273,41 @@ Libraries allow us to apply Python to real tasks. These are a few popular ones,
-- :material-application-outline:{ .lg .middle } [__Tkinter__](tkinter.md) [:material-language-python:](tkinter.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
+- :material-application-outline:{ .lg .middle } [__Tkinter__](tkinter.md)
+[:material-language-python:](tkinter.md){ .pt-lib-badge .pt-lib-badge--builtin title="Built-in — included with Python" }
Creating desktop applications: text, buttons, dropdowns, forms, output, etc.
[**`Tk`**](tkinter.md#creating-a-window)
- [**`Button`**](tkinter.md#widgets): [`Button`](tkinter.md#button) [`Entry`](tkinter.md#entry) [`Label`](tkinter.md#label)
+ [**`Button`**](tkinter.md#widgets):
+ [`Button`](tkinter.md#button)
+ [`Entry`](tkinter.md#entry)
+ [`Label`](tkinter.md#label)
- [**`pack`**](tkinter.md#layout-managers): [`grid`](tkinter.md#grid) [`pack`](tkinter.md#pack)
+ [**`pack`**](tkinter.md#layout-managers):
+ [`grid`](tkinter.md#grid)
+ [`pack`](tkinter.md#pack)
- [**`configure`**](tkinter.md#configuring-widgets): [`reading and changing options`](tkinter.md#reading-and-changing-options)
+ [**`configure`**](tkinter.md#configuring-widgets):
+ [`reading and changing options`](tkinter.md#reading-and-changing-options)
- [**`command`**](tkinter.md#handling-events): [`binding events`](tkinter.md#binding-events) [`command callbacks`](tkinter.md#command-callbacks)
+ [**`command`**](tkinter.md#handling-events):
+ [`binding events`](tkinter.md#binding-events)
+ [`command callbacks`](tkinter.md#command-callbacks)
- [**`Style`**](tkinter.md#styling-with-ttk): [`customizing a style`](tkinter.md#customizing-a-style)
+ [**`Style`**](tkinter.md#styling-with-ttk):
+ [`customizing a style`](tkinter.md#customizing-a-style)
- [**`messagebox`**](tkinter.md#dialogs): [`file dialogs`](tkinter.md#file-dialogs) [`message boxes`](tkinter.md#message-boxes)
+ [**`messagebox`**](tkinter.md#dialogs):
+ [`file dialogs`](tkinter.md#file-dialogs)
+ [`message boxes`](tkinter.md#message-boxes)
- [**`winfo_width`**](tkinter.md#introspecting-widgets): [`winfo methods`](tkinter.md#winfo-methods)
+ [**`winfo_width`**](tkinter.md#introspecting-widgets):
+ [`winfo methods`](tkinter.md#winfo-methods)
- [**`putting it together`**](tkinter.md#putting-it-together): [`a simple form`](tkinter.md#a-simple-form)
+ [**`putting it together`**](tkinter.md#putting-it-together):
+ [`a simple form`](tkinter.md#a-simple-form)
@@ -218,27 +317,43 @@ Libraries allow us to apply Python to real tasks. These are a few popular ones,
-- :material-face-recognition:{ .lg .middle } [__OpenCV__](opencv.md) [:material-download-outline:](opencv.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
+- :material-face-recognition:{ .lg .middle } [__OpenCV__](opencv.md)
+[:material-download-outline:](opencv.md){ .pt-lib-badge .pt-lib-badge--third-party title="Third-party — install separately with pip" }
Real-time image and video analysis, built directly on NumPy arrays: color spaces, edge detection, face detection.
- [**`reading, displaying, saving images`**](opencv.md#reading-displaying-and-saving-images): [`displaying a window`](opencv.md#displaying-a-window) [`imread`](opencv.md#reading-a-file) [`saving a file`](opencv.md#saving-a-file)
+ [**`reading, displaying, saving images`**](opencv.md#reading-displaying-and-saving-images):
+ [`displaying a window`](opencv.md#displaying-a-window)
+ [`imread`](opencv.md#reading-a-file)
+ [`saving a file`](opencv.md#saving-a-file)
- [**`drawing`**](opencv.md#drawing-shapes-and-text): [`shapes and lines`](opencv.md#shapes-and-lines) [`text`](opencv.md#text)
+ [**`drawing`**](opencv.md#drawing-shapes-and-text):
+ [`shapes and lines`](opencv.md#shapes-and-lines)
+ [`text`](opencv.md#text)
- [**`color spaces`**](opencv.md#color-spaces): [`cvtColor`](opencv.md#converting-color-spaces)
+ [**`color spaces`**](opencv.md#color-spaces):
+ [`cvtColor`](opencv.md#converting-color-spaces)
- [**`CascadeClassifier`**](opencv.md#face-detection-with-cascade-classifiers): [`detecting and labeling faces`](opencv.md#detecting-and-labeling-faces)
+ [**`CascadeClassifier`**](opencv.md#face-detection-with-cascade-classifiers):
+ [`detecting and labeling faces`](opencv.md#detecting-and-labeling-faces)
- [**`VideoCapture`**](opencv.md#working-with-video): [`reading frames`](opencv.md#reading-frames)
+ [**`VideoCapture`**](opencv.md#working-with-video):
+ [`reading frames`](opencv.md#reading-frames)
- [**`basic operations`**](opencv.md#basic-operations): [`cropping`](opencv.md#cropping) [`resize`](opencv.md#resize) [`rotating`](opencv.md#rotating)
+ [**`basic operations`**](opencv.md#basic-operations):
+ [`cropping`](opencv.md#cropping)
+ [`resize`](opencv.md#resize)
+ [`rotating`](opencv.md#rotating)
- [**`thresholding, edge detection`**](opencv.md#thresholding-and-edge-detection): [`Canny`](opencv.md#edge-detection) [`threshold`](opencv.md#threshold)
+ [**`thresholding, edge detection`**](opencv.md#thresholding-and-edge-detection):
+ [`Canny`](opencv.md#edge-detection)
+ [`threshold`](opencv.md#threshold)
- [**`blurring`**](opencv.md#blurring): [`gaussian blur`](opencv.md#gaussian-blur)
+ [**`blurring`**](opencv.md#blurring):
+ [`gaussian blur`](opencv.md#gaussian-blur)
- [**`contours`**](opencv.md#contours): [`finding and drawing contours`](opencv.md#finding-and-drawing-contours)
+ [**`contours`**](opencv.md#contours):
+ [`finding and drawing contours`](opencv.md#finding-and-drawing-contours)
diff --git a/docs/oop.md b/docs/oop.md
index ec790d9..33a6442 100644
--- a/docs/oop.md
+++ b/docs/oop.md
@@ -1,7 +1,7 @@
---
description: >-
Python classes and object-oriented programming explained with runnable examples:
- attributes, methods, and inheritance.
+ attributes, methods, property/staticmethod/classmethod, and inheritance.
---
# :material-package-variant:{ .lg .middle } Classes & Object-oriented programming (OOP)
@@ -231,6 +231,78 @@ ball.describe() # "a 5 ft ball python"
+## Method decorators
+
+Python provides three built-in [decorators](functions.md#decorators) for methods that change how the method is called and add functionality:
+
+```python-ref
+class Snake:
+ def __init__(self, species, length_ft):
+ self.species = species
+ self.length_ft = length_ft
+
+ @property # a computed attribute
+ def length_cm(self): # will be called like an attribute, not a method
+ return self.length_ft * 30.48
+
+ @staticmethod # a class-level utility
+ def is_valid_length(length_ft): # no self — doesn't need an object
+ return length_ft > 0
+
+ @classmethod # an alternate constructor to __init__
+ def from_cm(cls, species, length_cm): # receives cls (the class) instead of self
+ return cls(species, length_cm / 30.48)
+
+ball = Snake("ball", 5)
+ball.length_cm # 152.4 — called like an attribute, no parentheses
+Snake.is_valid_length(5) # True — called on the class, no object needed
+Snake.from_cm("ball", 152.4).length_ft # 5.0 — builds a new object instead of modifying one
+```
+
+### @property
+
+Call it like a plain attribute, no parentheses. Turns a method into a value computed fresh every time it's read, instead of stored and going stale — `length_cm` below always reflects the current `length_ft`, even if it changes later.
+
+Use it for a value that's cheap to derive from existing attributes and should look like a plain attribute to the rest of the code; skip it if the computation is expensive to redo on every access, or needs its own arguments beyond `self`.
+
+??? tip "Property setters"
+ A property is read-only by default — assigning to it raises an error unless you also define a setter with `@x.setter`, named the same as the property.
+
+ ```python-ref
+ class Snake:
+ def __init__(self, species, length_ft):
+ self.species = species
+ self.length_ft = length_ft
+
+ @property
+ def length_cm(self):
+ return self.length_ft * 30.48
+
+ @length_cm.setter
+ def length_cm(self, value):
+ self.length_ft = value / 30.48
+
+ ball = Snake("ball", 5)
+ ball.length_cm = 304.8 # runs the setter, which updates length_ft
+ ball.length_ft # 10.0
+ ```
+
+### @staticmethod
+
+Call it without needing an object at all, directly on the class. Removes the automatic `self`, so the method can't read or change any object's data — it's really just a plain function, grouped under the class because it's conceptually related.
+
+Use it for logic tied to the class's purpose but not to any one object's state, like a validation check; if it needs `self`, it should be a regular method instead.
+
+### @classmethod
+
+Call it as an alternative way to build an object. Receives the class itself (conventionally named `cls`) instead of an object, so it can construct and return a new instance.
+
+Use it when there's more than one sensible way to build an object — `Snake.from_cm(...)` alongside the usual `Snake(...)` — as a second, clearly-named constructor; skip it if there's only one way to build the object, since `__init__()` would be complete.
+
+
+
+
+
## Inheritance
A child class reuses — and can extend or override — everything defined in a parent class, instead of rewriting it from scratch. The parent is also called the **base class**; the child is the **derived class**.
diff --git a/docs/style.md b/docs/style.md
index bd46fa3..8b4c4f8 100644
--- a/docs/style.md
+++ b/docs/style.md
@@ -21,10 +21,10 @@ A few things worth double-checking before calling a script finished — each lin
- [ ] **[Run a linter check](#linter-tool)** — catches many of the following automatically, but it can be good practice to check manually instead to get familiar with writing it correctly from the start:
- [ ] **[File Order](#file-order)** — standardized file layout
- - [ ] **[Mutable default arguments](#common-patterns)** — a default list/dict shared across every call
+ - [ ] **[Mutable default arguments](functions.md#defining-a-function)** — a default list/dict shared across every call
- [ ] **[`is None` instead of `== None`](#common-patterns)** — a real correctness risk, not just style
- - [ ] **[`with open(...)` instead of manual `open()`/`close()`](#common-patterns)** — avoids a file left open if something goes wrong
- - [ ] **[Catch specific exceptions](#catch-specific-exceptions)** — no bare `except:` swallowing errors you didn't expect
+ - [ ] **[`with open(...)` instead of manual `open()`/`close()`](files.md#opening-a-file)** — avoids a file left open if something goes wrong
+ - [ ] **[Catch specific exceptions](errors.md#catch-specific-exceptions)** — no bare `except:` swallowing errors you didn't expect
- [ ] **[Naming](#naming)** — does every variable and function name say what it holds?
- [ ] **[Docstrings](#docstrings)** — does every function and file explain what it does?
- [ ] **[Truthy checks instead of `len(x) > 0`](#common-patterns)** — test a collection directly
@@ -33,13 +33,13 @@ A few things worth double-checking before calling a script finished — each lin
- [ ] **[Blank lines](#blank-lines)** — two around top-level functions/classes, one between methods
- [ ] **[Whitespace](#whitespace)** — spaces around operators, but not around a keyword argument's `=`
- [ ] **[Comments](#comments)** — two spaces before an inline `#`, one space after
-- [ ] **[Keep functions focused](#keep-functions-focused)** — does each function do just one job, with no repeated logic a [linter](#linter-tool) won't flag on its own?
+- [ ] **[Keep functions focused](functions.md#keep-functions-focused)** — does each function do just one job, with no repeated logic a [linter](#linter-tool) won't flag on its own?
- [ ] **[Constants](#constants)** — are unchanging numbers pulled out into named `ALL_CAPS` values?
- [ ] **[Quote style](#quote-style)** — one quote style used consistently throughout the file
-- [ ] **[Type hints](#type-hints)** — used on a function signature where the types aren't obvious?
-- [ ] **[Tuple unpacking instead of a temporary variable](#common-patterns)** — swapping two variables directly
+- [ ] **[Type hints](functions.md#type-hints)** — used on a function signature where the types aren't obvious?
+- [ ] **[Tuple unpacking instead of a temporary variable](collections.md#packing-and-unpacking)** — swapping two variables directly
- [ ] **[File names](workspace.md#step-2-write-and-run-a-python-file)** — `snake_case.py`, no hyphens or spaces
-- [ ] **[Readable print output](#readable-print-output)** — `\n`/`\t` and separator rows used to space out console output
+- [ ] **[Escape sequences](foundations.md#escape-sequences)** — `\n`/`\t` and separator rows used to space out console output
@@ -296,17 +296,6 @@ A few of these a beginner tends to write out longhand before learning the built-
print(i, s)
```
-- **`with open(...)` instead of a manual `open()`/`close()` pair** — a context manager guarantees the file gets closed even if something goes wrong partway through
-
- ```python-ref
- file = open("notes.txt") # works, but there's a risk of locking the file in a buffer
- contents = file.read()
- file.close()
-
- with open("notes.txt") as file: # Pythonic — closes automatically, even on error
- contents = file.read()
- ```
-
- **`is None` instead of `== None`** — checking against `None` is a check of identity, not equality, so `is` is the correct tool
```python-ref
@@ -318,120 +307,12 @@ A few of these a beginner tends to write out longhand before learning the built-
print("unknown length")
```
-- **Avoid mutable default arguments** — a default list or dict is created once, when the function is defined, and reused across every call — so items appended in one call are still there the next time, unless the default is `None` instead
-
- ```python-ref
- def add_snake(species, tracked=[]): # works, but tracked is shared across every call
- tracked.append(species)
- return tracked
-
- def add_snake(species, tracked=None): # Pythonic — a fresh list every call
- if tracked is None:
- tracked = []
- tracked.append(species)
- return tracked
- ```
-
-- **Tuple unpacking instead of a temporary variable** — swap two variables directly, rather than juggling a spare variable to hold one during the swap
-
- ```python-ref
- a, b = "ball python", "boa"
- temp = a # manual swap using a spare variable
- a = b
- b = temp
-
- a, b = b, a # Pythonic — tuple unpacking swaps directly
- ```
-
## Additional best practices
-### Keep functions focused
-
-A function should do one thing. If you find yourself describing it with "and" — "parses the input *and* saves it *and* prints a summary" — it's probably three functions.
-
-```python-ref
-def parse_and_save(text): # doing too much
- ...
-
-def parse_entry(text): # one job each
- ...
-
-def save_entry(entry):
- ...
-```
-
-Repeating the same few lines in multiple places is a sign to pull them into their own function instead — commonly called **DRY** ("don't repeat yourself"). It also means a fix only has to happen in one place, instead of every place the lines were copied to.
-
-??? tip "Guard clauses: return early instead of nesting"
- Handle the exception case first and return, rather than wrapping the rest of the function in an `else`. It keeps the normal path at the lowest indentation level, instead of nested one level deeper for every added check.
-
- ```python-ref
- def describe(length_ft):
- if length_ft > 0:
- return f"{length_ft} ft"
- else:
- return "unknown length"
- ```
-
- ```python-ref
- def describe(length_ft):
- if length_ft <= 0:
- return "unknown length"
- return f"{length_ft} ft"
- ```
-
- Both versions do the same thing — the second reads top to bottom without having to track which `if` branch you're inside.
-
-### Catch specific exceptions
-
-Catch the exact exception you expect (`except ValueError:`) instead of a bare `except:` — a bare `except` also silently swallows errors you didn't anticipate, including a typo in your own code, and even catches things like a keyboard interrupt (++ctrl+c++) that usually shouldn't be caught at all. Full `try`/`except` mechanics are covered on the [Errors](errors.md#catch-with-tryexcept) page.
-
-```python-ref
-try:
- length_ft = float(user_input)
-except: # catches everything, even mistakes you didn't expect
- print("invalid input")
-
-try:
- length_ft = float(user_input)
-except ValueError: # only catches what you actually expect
- print("invalid input")
-```
-
-### Type hints
-
-A **type hint** annotates a parameter or return value with the type it's expected to be — `species: str`, `length_ft: float`, `-> bool` — without Python enforcing it at runtime; it's documentation an editor or a separate type checker (like `mypy`) can check for you.
-
-```python-ref
-def is_unusually_long(species: str, length_ft: float) -> bool:
- return length_ft > 5
-```
-
-A wrong type still runs — Python doesn't stop you from calling `is_unusually_long("ball python", "4.5")` with a string instead of a `float` — the hint only helps a tool catch the mismatch before you do, and helps a reader (or their editor) see what's expected without reading the function body.
-
-### Readable print output
-
-`\n` and `\t` are **escape sequences** — `\n` inserts a line break, `\t` a tab — so a single `print()` call can space out multi-line or columned output.
-
-```python
-print(f"species: burmese\nlength: 10 ft\n")
-print("species\t\tlength_ft")
-print("ball python\t4.5")
-```
-
-A row of repeated characters makes a quick visual separator between sections of console output, useful for breaking up a long script's output into readable chunks.
-
-```python
-print("survey results")
-print("=" * 40)
-```
-
-### Going further { data-card-link="skip" }
-
??? tip "Be creative with ASCII art"
Write in the terminal with bubble letters or draw images through creative character use.
@@ -460,5 +341,6 @@ print("=" * 40)
[ascii text resource](https://patorjk.com/software/taag/#p=display&f=Isometric1&t=Type+Something+&x=none&v=4&h=4&w=80&we=false)
[ascii art resource](https://www.asciiart.eu/#google_vignette)
+
diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css
index 3af33f6..8a09076 100644
--- a/docs/stylesheets/extra.css
+++ b/docs/stylesheets/extra.css
@@ -17,8 +17,7 @@
/* background of the homepage category container ("outer grid" boxes); the
library-card corner wedge + border track this. */
--pt-section-bg: color-mix(in srgb, var(--pt-bg), color-mix(in srgb, var(--pt-panel) 84%, var(--pt-ink)));
- /* a punchier, more saturated green than the old #185B37 */
- --pt-accent: #0B7038;
+ --pt-accent: #08542A;
/* card description text + the library-card badge icon */
--pt-desc-blue: #255C6E;
--pt-heading-h2: #004C24;
@@ -933,6 +932,17 @@ input:checked + .md-consent__settings {
color: var(--pt-danger);
}
+/* .twemoji is display:inline-flex in Material's own CSS, an atomic inline
+ box that a wrapping 's text-decoration underline can't draw through —
+ so the hover underline on .pt-jump-table td a (see below) never showed for
+ icon-only cells. A bottom box-shadow fakes the same underline without
+ taking up layout space, colored to match the icon via currentColor.
+ Only :material-check: is ever linked — :material-close: cells aren't
+ clickable, since "not applicable" has nowhere useful to jump to. */
+.pt-jump-table td a:hover .pt-icon-success {
+ box-shadow: 0 0.125em 0 0 currentColor;
+}
+
/* Feedback form on about.md, posting to Formspree */
.pt-feedback-form {
display: flex;
diff --git a/includes/glossary.md b/includes/glossary.md
index 29a2599..3e5fc0c 100644
--- a/includes/glossary.md
+++ b/includes/glossary.md
@@ -1,7 +1,13 @@
*[exception]: Python's formal term for the type of error that was raised, like KeyError or ValueError
*[Exception]: Python's formal term for the type of error that was raised, like KeyError or ValueError
+*[expensive]: Takes a relatively long time or a lot of memory to run — not about money
+*[Expensive]: Takes a relatively long time or a lot of memory to run — not about money
*[constructor]: The method that runs automatically to set up a new object's starting values
*[Constructor]: The method that runs automatically to set up a new object's starting values
+*[object]: A specific thing built from a class, with its own independent copy of that class's data
+*[Object]: A specific thing built from a class, with its own independent copy of that class's data
+*[instance]: Another word for an object — a specific thing built from a class, with its own independent copy of that class's data
+*[Instance]: Another word for an object — a specific thing built from a class, with its own independent copy of that class's data
*[boolean mask]: A same-size array of True/False values, used to filter another array or column down to just the matching rows
*[Boolean mask]: A same-size array of True/False values, used to filter another array or column down to just the matching rows
*[iterable]: Anything that can hand back its items one at a time — a list, string, range, dict, and more — whether you're looping over it, converting it, or unpacking it
@@ -20,6 +26,8 @@
*[Block]: A group of indented lines under a colon that Python runs together as one unit — the body of an if, loop, function, or class
*[convention]: An agreed-upon way of doing something that Python doesn't enforce, followed anyway so code stays predictable to other readers
*[Convention]: An agreed-upon way of doing something that Python doesn't enforce, followed anyway so code stays predictable to other readers
+*[conventionally]: By convention — an agreed-upon way of doing something that Python doesn't enforce, followed anyway so code stays predictable to other readers
+*[Conventionally]: By convention — an agreed-upon way of doing something that Python doesn't enforce, followed anyway so code stays predictable to other readers
*[truthy]: Counts as True when used somewhere a bool is expected, even though the value itself isn't actually True — every value is either truthy or falsy
*[Truthy]: Counts as True when used somewhere a bool is expected, even though the value itself isn't actually True — every value is either truthy or falsy
*[falsy]: Counts as False when used somewhere a bool is expected, even though the value itself isn't actually False — every value is either truthy or falsy
@@ -49,3 +57,9 @@
*[Bug]: A mistake in your code that makes it do the wrong thing, whether or not Python actually notices and raises an error
*[syntax]: The grammatical rules for what counts as validly structured code, checked before any of it runs — independent of whether the logic is actually correct
*[Syntax]: The grammatical rules for what counts as validly structured code, checked before any of it runs — independent of whether the logic is actually correct
+*[callable]: Can be called with parentheses to run it, the way a function can — includes functions, classes, and any object with a __call__ method
+*[Callable]: Can be called with parentheses to run it, the way a function can — includes functions, classes, and any object with a __call__ method
+*[graceful]: Handling a failure without crashing or losing data — continuing on, showing a clear message, or falling back to a default instead of stopping abruptly
+*[Graceful]: Handling a failure without crashing or losing data — continuing on, showing a clear message, or falling back to a default instead of stopping abruptly
+*[gracefully]: In a way that handles a failure without crashing or losing data — continuing on, showing a clear message, or falling back to a default instead of stopping abruptly
+*[Gracefully]: In a way that handles a failure without crashing or losing data — continuing on, showing a clear message, or falling back to a default instead of stopping abruptly