-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexample_sources_data.py
More file actions
2 lines (2 loc) · 286 KB
/
Copy pathexample_sources_data.py
File metadata and controls
2 lines (2 loc) · 286 KB
1
2
# Generated by scripts/embed_example_sources.py. Do not edit by hand.
EXAMPLE_SOURCE_FILES = {'manifest.toml': 'python_version = "3.13"\ndocs_base_url = "https://docs.python.org/3.13"\n\norder = [\n "hello-world",\n "values",\n "literals",\n "numbers",\n "booleans",\n "operators",\n "none",\n "variables",\n "constants",\n "truthiness",\n "equality-and-identity",\n "mutability",\n "object-lifecycle",\n "strings",\n "bytes-and-bytearray",\n "string-formatting",\n "conditionals",\n "guard-clauses",\n "assignment-expressions",\n "for-loops",\n "break-and-continue",\n "loop-else",\n "iterating-over-iterables",\n "iterators",\n "iterator-vs-iterable",\n "sentinel-iteration",\n "match-statements",\n "advanced-match-patterns",\n "while-loops",\n "lists",\n "tuples",\n "unpacking",\n "dicts",\n "sets",\n "slices",\n "comprehensions",\n "comprehension-patterns",\n "sorting",\n "collections-module",\n "copying-collections",\n "functions",\n "keyword-only-arguments",\n "positional-only-parameters",\n "args-and-kwargs",\n "multiple-return-values",\n "closures",\n "partial-functions",\n "scope-global-nonlocal",\n "recursion",\n "lambdas",\n "generators",\n "yield-from",\n "generator-expressions",\n "itertools",\n "decorators",\n "classes",\n "inheritance-and-super",\n "classmethods-and-staticmethods",\n "dataclasses",\n "properties",\n "special-methods",\n "truth-and-size",\n "container-protocols",\n "callable-objects",\n "operator-overloading",\n "attribute-access",\n "bound-and-unbound-methods",\n "descriptors",\n "metaclasses",\n "context-managers",\n "delete-statements",\n "exceptions",\n "assertions",\n "exception-chaining",\n "exception-groups",\n "warnings",\n "modules",\n "import-aliases",\n "packages",\n "virtual-environments",\n "type-hints",\n "runtime-type-checks",\n "union-and-optional-types",\n "type-aliases",\n "typed-dicts",\n "structured-data-shapes",\n "literal-and-final",\n "callable-types",\n "generics-and-typevar",\n "paramspec",\n "overloads",\n "casts-and-any",\n "newtype",\n "protocols",\n "abstract-base-classes",\n "enums",\n "regular-expressions",\n "number-parsing",\n "custom-exceptions",\n "json",\n "logging",\n "testing",\n "subprocesses",\n "threads-and-processes",\n "networking",\n "datetime",\n "csv-data",\n "async-await",\n "async-iteration-and-context",\n]\n', 'abstract-base-classes.md': '+++\nslug = "abstract-base-classes"\ntitle = "Abstract Base Classes"\nsection = "Classes"\nsummary = "ABC and abstractmethod enforce that subclasses implement required methods."\ndoc_path = "/library/abc.html"\nsee_also = [\n "protocols",\n "inheritance-and-super",\n "classes",\n]\n+++\n\n`ABC` and `@abstractmethod` describe an interface that subclasses must implement. The base class refuses to instantiate until a concrete subclass provides every abstract method, which catches "I forgot to implement this" at construction time rather than at the first method call.\n\nABCs are different from `Protocol`. An ABC is nominal: a class participates in the contract by inheriting from it. A `Protocol` is structural: any class with the right methods qualifies, no inheritance required. Reach for an ABC when you want shared implementation in the base class or you want `isinstance()` to mean "explicitly opted in"; reach for a `Protocol` when you only care about behavior at the API boundary.\n\nThe cost is a small amount of ceremony at the type level. The benefit is that a half-implemented subclass cannot be created by accident.\n\n:::program\n```python\nfrom abc import ABC, abstractmethod\nfrom typing import Protocol\n\nclass Shape(ABC):\n @abstractmethod\n def area(self) -> float:\n ...\n\n def describe(self) -> str:\n return f"shape with area {self.area()}"\n\ntry:\n Shape()\nexcept TypeError as error:\n print(error)\n\nclass Square(Shape):\n def __init__(self, side):\n self.side = side\n\n def area(self):\n return self.side ** 2\n\nprint(Square(3).area())\nprint(Square(3).describe())\n\nclass Incomplete(Shape):\n pass\n\ntry:\n Incomplete()\nexcept TypeError as error:\n print(error)\n\nclass HasArea(Protocol):\n def area(self) -> float:\n ...\n\nclass Triangle:\n def __init__(self, base, height):\n self.base = base\n self.height = height\n\n def area(self):\n return 0.5 * self.base * self.height\n\ndef total_area(shapes: list[HasArea]) -> float:\n return sum(shape.area() for shape in shapes)\n\nprint(total_area([Square(3), Triangle(4, 3)]))\nprint(isinstance(Triangle(4, 3), Shape))\nprint(isinstance(Square(3), Shape))\n```\n:::\n\n:::cell\n`ABC` plus `@abstractmethod` declares the contract. Trying to construct the base class itself fails because at least one method has no implementation. A concrete `describe()` lives alongside the abstract `area()` so subclasses inherit shared behavior for free.\n\n```python\nfrom abc import ABC, abstractmethod\n\nclass Shape(ABC):\n @abstractmethod\n def area(self) -> float:\n ...\n\n def describe(self) -> str:\n return f"shape with area {self.area()}"\n\ntry:\n Shape()\nexcept TypeError as error:\n print(error)\n```\n\n```output\nCan\'t instantiate abstract class Shape without an implementation for abstract method \'area\'\n```\n:::\n\n:::cell\nA subclass that implements every abstract method is concrete and can be instantiated. It also inherits the non-abstract methods from the base class.\n\n```python\nclass Square(Shape):\n def __init__(self, side):\n self.side = side\n\n def area(self):\n return self.side ** 2\n\nprint(Square(3).area())\nprint(Square(3).describe())\n```\n\n```output\n9\nshape with area 9\n```\n:::\n\n:::cell\nA subclass that forgets to implement an abstract method also cannot be instantiated — that is the value the ABC adds. The error fires at construction, not when something later tries to call the missing method.\n\n```python\nclass Incomplete(Shape):\n pass\n\ntry:\n Incomplete()\nexcept TypeError as error:\n print(error)\n```\n\n```output\nCan\'t instantiate abstract class Incomplete without an implementation for abstract method \'area\'\n```\n:::\n\n:::cell\nContrast with `Protocol`. A `HasArea` protocol accepts any class with an `area()` method, no inheritance required. `Triangle` does not inherit from `Shape`, so it satisfies the protocol but fails `isinstance(_, Shape)`. `Square` satisfies both because it explicitly inherited from the ABC.\n\n```python\nfrom typing import Protocol\n\nclass HasArea(Protocol):\n def area(self) -> float:\n ...\n\nclass Triangle:\n def __init__(self, base, height):\n self.base = base\n self.height = height\n\n def area(self):\n return 0.5 * self.base * self.height\n\ndef total_area(shapes: list[HasArea]) -> float:\n return sum(shape.area() for shape in shapes)\n\nprint(total_area([Square(3), Triangle(4, 3)]))\nprint(isinstance(Triangle(4, 3), Shape))\nprint(isinstance(Square(3), Shape))\n```\n\n```output\n15.0\nFalse\nTrue\n```\n:::\n\n:::note\n- `ABC` plus `@abstractmethod` blocks instantiation until every abstract method has an implementation.\n- ABCs are nominal — subclasses opt in by inheriting; `isinstance()` reflects that opt-in.\n- Protocols are structural — any class with the right shape qualifies, regardless of inheritance.\n- Prefer an ABC when shared implementation or explicit opt-in matters; prefer a Protocol when only behavior at the API boundary matters.\n:::\n', 'advanced-match-patterns.md': '+++\nslug = "advanced-match-patterns"\ntitle = "Advanced Match Patterns"\nsection = "Control Flow"\nsummary = "match patterns can destructure sequences, combine alternatives, and add guards."\ndoc_path = "/tutorial/controlflow.html#match-statements"\nsee_also = [\n "match-statements",\n "tuples",\n "classes",\n]\n+++\n\nStructural pattern matching is more than equality checks. Patterns can destructure sequences, match several alternatives, capture the rest of a sequence, and use guards.\n\nUse these forms when the shape of data is the decision. If the decision is only a single boolean condition, ordinary `if` statements are usually clearer.\n\nThe wildcard `_` catches everything not matched earlier.\n\n:::program\n```python\ndef describe(command):\n match command:\n case ["move", x, y] if x >= 0 and y >= 0:\n return f"move to {x},{y}"\n case ["quit" | "exit"]:\n return "stop"\n case ["echo", *words]:\n return " ".join(words)\n case _:\n return "unknown"\n\nprint(describe(["move", 2, 3]))\nprint(describe(["exit"]))\nprint(describe(["echo", "hello", "python"]))\nprint(describe(["move", -1, 3]))\n```\n:::\n\n:::cell\nSequence patterns match by position. A guard after `if` adds a condition that must also be true.\n\n```python\ndef describe(command):\n match command:\n case ["move", x, y] if x >= 0 and y >= 0:\n return f"move to {x},{y}"\n case ["quit" | "exit"]:\n return "stop"\n case ["echo", *words]:\n return " ".join(words)\n case _:\n return "unknown"\n\nprint(describe(["move", 2, 3]))\n```\n\n```output\nmove to 2,3\n```\n:::\n\n:::cell\nAn OR pattern accepts several alternatives in one case. A star pattern captures the rest of a sequence.\n\n```python\nprint(describe(["exit"]))\nprint(describe(["echo", "hello", "python"]))\n```\n\n```output\nstop\nhello python\n```\n:::\n\n:::cell\nThe wildcard `_` catches values that did not match earlier cases. Here the guard rejects the negative coordinate.\n\n```python\nprint(describe(["move", -1, 3]))\n```\n\n```output\nunknown\n```\n:::\n\n:::note\n- Use `case _` as a wildcard fallback.\n- Guards refine a pattern after the structure matches.\n- OR patterns and star patterns keep shape-based branches compact.\n:::\n', 'args-and-kwargs.md': '+++\nslug = "args-and-kwargs"\ntitle = "Args and Kwargs"\nsection = "Functions"\nsummary = "*args collects extra positional arguments and **kwargs collects named ones."\ndoc_path = "/tutorial/controlflow.html#arbitrary-argument-lists"\nsee_also = [\n "functions",\n "keyword-only-arguments",\n "partial-functions",\n "paramspec",\n]\n+++\n\n`*args` and `**kwargs` let a function accept flexible positional and keyword arguments. They are the function-definition counterpart to unpacking at a call site.\n\nThese parameters are useful for wrappers, decorators, logging helpers, and APIs that forward arguments to another function.\n\nThey should not replace clear signatures. If a function has a stable interface, explicit parameters document expectations better than a bag of arguments.\n\n:::program\n```python\ndef total(*numbers):\n return sum(numbers)\n\nprint(total(2, 3, 5))\n\n\ndef describe(**metadata):\n print(metadata)\n\ndescribe(owner="Ada", public=True)\n\n\ndef report(title, *items, **metadata):\n print(title)\n print(items)\n print(metadata)\n\nreport("scores", 10, 9, owner="Ada")\n```\n:::\n\n:::cell\n`*args` collects extra positional arguments into a tuple. This fits functions that naturally accept any number of similar values.\n\n```python\ndef total(*numbers):\n return sum(numbers)\n\nprint(total(2, 3, 5))\n```\n\n```output\n10\n```\n:::\n\n:::cell\n`**kwargs` collects named arguments into a dictionary. The names become string keys.\n\n```python\ndef describe(**metadata):\n print(metadata)\n\ndescribe(owner="Ada", public=True)\n```\n\n```output\n{\'owner\': \'Ada\', \'public\': True}\n```\n:::\n\n:::cell\nA function can combine explicit parameters, `*args`, and `**kwargs`. Put the flexible parts last so the fixed shape remains visible.\n\n```python\ndef report(title, *items, **metadata):\n print(title)\n print(items)\n print(metadata)\n\nreport("scores", 10, 9, owner="Ada")\n```\n\n```output\nscores\n(10, 9)\n{\'owner\': \'Ada\'}\n```\n:::\n\n:::note\n- Use these tools when a function naturally accepts a flexible shape.\n- Prefer explicit parameters when the accepted arguments are known and fixed.\n- `*args` is a tuple; `**kwargs` is a dictionary.\n:::\n', 'assertions.md': '+++\nslug = "assertions"\ntitle = "Assertions"\nsection = "Errors"\nsummary = "assert documents internal assumptions and fails loudly when they are false."\ndoc_path = "/reference/simple_stmts.html#the-assert-statement"\nsee_also = [\n "exceptions",\n "custom-exceptions",\n "type-hints",\n]\n+++\n\n`assert` checks an internal assumption. If the condition is false, Python raises `AssertionError` with an optional message.\n\nUse assertions for programmer assumptions, not for validating user input or external data. Input validation should raise ordinary exceptions that production code expects to handle.\n\nAssertions make invariants executable while keeping the successful path compact.\n\n:::program\n```python\ndef average(scores):\n assert scores, "scores must not be empty"\n return sum(scores) / len(scores)\n\nprint(average([8, 10]))\n\ntry:\n average([])\nexcept AssertionError as error:\n print(error)\n```\n:::\n\n:::cell\nWhen the assertion is true, execution continues normally. The assertion documents the function\'s internal expectation.\n\n```python\ndef average(scores):\n assert scores, "scores must not be empty"\n return sum(scores) / len(scores)\n\nprint(average([8, 10]))\n```\n\n```output\n9.0\n```\n:::\n\n:::cell\nWhen the assertion is false, Python raises `AssertionError`. This signals a broken assumption, not a normal recovery path.\n\n```python\ntry:\n average([])\nexcept AssertionError as error:\n print(error)\n```\n\n```output\nscores must not be empty\n```\n:::\n\n:::note\n- Use `assert` for internal invariants and debugging assumptions.\n- Use explicit exceptions for user input, files, network responses, and other expected failures.\n- Assertions can be disabled with Python optimization flags, so do not rely on them for security checks.\n:::\n', 'assignment-expressions.md': '+++\nslug = "assignment-expressions"\ntitle = "Assignment Expressions"\nsection = "Control Flow"\nsummary = "The walrus operator assigns a value inside an expression."\ndoc_path = "/reference/expressions.html#assignment-expressions"\nsee_also = [\n "conditionals",\n "while-loops",\n "variables",\n]\n+++\n\nThe assignment expression operator `:=` assigns a name while evaluating an expression. It is often called the walrus operator.\n\nUse it when computing a value and testing it are naturally one step. Avoid it when a separate assignment would make the code easier to read.\n\nThe boundary is readability: the walrus operator can remove duplication, but it should not hide important state changes.\n\n:::program\n```python\nmessages = ["hello", "", "python"]\n\nfor message in messages:\n if length := len(message):\n print(message, length)\n\nqueue = ["retry", "ok"]\nwhile (status := queue.pop(0)) != "ok":\n print(status)\nprint(status)\n```\n:::\n\n:::cell\nAn assignment expression can name a computed value while a condition tests it. Here empty strings are skipped because their length is zero.\n\n```python\nmessages = ["hello", "", "python"]\n\nfor message in messages:\n if length := len(message):\n print(message, length)\n```\n\n```output\nhello 5\npython 6\n```\n:::\n\n:::cell\nThe same idea works in loops that read state until a sentinel appears. The assignment and comparison stay together.\n\n```python\nqueue = ["retry", "ok"]\nwhile (status := queue.pop(0)) != "ok":\n print(status)\nprint(status)\n```\n\n```output\nretry\nok\n```\n:::\n\n:::note\n- `name := expression` assigns and evaluates to the assigned value.\n- Use it to avoid computing the same value twice.\n- Prefer a normal assignment when the expression becomes hard to scan.\n:::\n', 'async-await.md': '+++\nslug = "async-await"\ntitle = "Async Await"\nsection = "Async"\nsummary = "async def creates coroutines, and await pauses until awaitable work completes."\ndoc_path = "/library/asyncio-task.html"\nsee_also = [\n "async-iteration-and-context",\n "functions",\n "context-managers",\n]\n+++\n\n`async def` creates a coroutine function. Calling it creates a coroutine object; the body runs when an event loop awaits or schedules it.\n\n`await` pauses the current coroutine until another awaitable completes. This lets one event loop make progress on other work while a task waits for I/O.\n\nCloudflare Workers handlers are asynchronous, so understanding `await` is practical for fetch calls, bindings, and service interactions even when a small example uses `asyncio.sleep(0)` as a stand-in.\n\nThe alternative is ordinary `def` for work that completes immediately. Use async code for I/O-shaped waiting, not as a faster replacement for CPU-bound Python.\n\n:::program\n```python\nimport asyncio\n\ndef slug_to_title(slug):\n return slug.replace("-", " ").title()\n\nasync def fetch_title(slug):\n await asyncio.sleep(0)\n return slug_to_title(slug)\n\nasync def main():\n title = await fetch_title("async-await")\n print(title)\n titles = await asyncio.gather(fetch_title("json"), fetch_title("datetime"))\n print(titles)\n\nasyncio.run(main())\n\n\nclass Session:\n async def __aenter__(self):\n print("open")\n return self\n\n async def __aexit__(self, *_):\n print("close")\n return False\n\n\nasync def stream():\n for slug in ["json", "datetime"]:\n await asyncio.sleep(0)\n yield slug\n\n\nasync def driver():\n async with Session():\n async for slug in stream():\n print(slug)\n\nasyncio.run(driver())\n```\n:::\n\n:::cell\nAn ordinary `def` function computes its result immediately: calling it runs the body and hands the value straight back. This synchronous form is the baseline the rest of the page contrasts against.\n\n```python\ndef slug_to_title(slug):\n return slug.replace("-", " ").title()\n\nprint(slug_to_title("async-await"))\n```\n\n```output\nAsync Await\n```\n:::\n\n:::cell\nAn `async def` function returns a coroutine object when called. The function body has not produced its final result yet.\n\n```python\nimport asyncio\n\nasync def fetch_title(slug):\n await asyncio.sleep(0)\n return slug_to_title(slug)\n\ncoroutine = fetch_title("async-await")\nprint(coroutine.__class__.__name__)\ncoroutine.close()\n```\n\n```output\ncoroutine\n```\n:::\n\n:::cell\nUse `await` inside another coroutine to get the eventual result. `asyncio.run()` starts an event loop for the top-level coroutine.\n\n```python\nasync def main():\n title = await fetch_title("async-await")\n print(title)\n\nasyncio.run(main())\n```\n\n```output\nAsync Await\n```\n:::\n\n:::cell\n`asyncio.gather()` awaits several awaitables and returns their results in order. This is the shape used when independent I/O operations can progress together.\n\n```python\nasync def main():\n titles = await asyncio.gather(fetch_title("json"), fetch_title("datetime"))\n print(titles)\n\nasyncio.run(main())\n```\n\n```output\n[\'Json\', \'Datetime\']\n```\n:::\n\n:::cell\n`async with` and `async for` are the asynchronous forms of context managers and iteration. A class implements `__aenter__`/`__aexit__` to act as an async context manager; an `async def` function with `yield` becomes an async generator. The dedicated [async iteration and context](/examples/async-iteration-and-context) page explains the protocols in depth.\n\n```python\nclass Session:\n async def __aenter__(self):\n print("open")\n return self\n\n async def __aexit__(self, *_):\n print("close")\n return False\n\n\nasync def stream():\n for slug in ["json", "datetime"]:\n await asyncio.sleep(0)\n yield slug\n\n\nasync def driver():\n async with Session():\n async for slug in stream():\n print(slug)\n\nasyncio.run(driver())\n```\n\n```output\nopen\njson\ndatetime\nclose\n```\n:::\n\n:::note\n- Calling an async function creates a coroutine object.\n- `await` yields control until an awaitable completes.\n- Workers request handlers are async, so this pattern appears around fetches and bindings.\n- Prefer ordinary functions when there is no awaitable work to coordinate.\n:::\n', 'async-iteration-and-context.md': '+++\nslug = "async-iteration-and-context"\ntitle = "Async Iteration and Context"\nsection = "Async"\nsummary = "async for and async with consume asynchronous streams and cleanup protocols."\ndoc_path = "/reference/compound_stmts.html#async-for"\nsee_also = [\n "async-await",\n "iterators",\n "context-managers",\n]\n+++\n\n`async for` consumes an asynchronous iterator: a stream whose next value may require `await`. `async with` surrounds a block with asynchronous setup and cleanup.\n\nThese forms appear around network streams, database cursors, locks, and service clients where both iteration and cleanup may wait on I/O.\n\nUse ordinary `for` and `with` when producing the next value or cleaning up does not need to await anything.\n\nThe syntax mirrors `for` and `with`, but the protocol methods are asynchronous.\n\n:::program\n```python\nimport asyncio\n\nasync def titles():\n for slug in ["values", "async-await"]:\n await asyncio.sleep(0)\n yield slug.replace("-", " ").title()\n\nclass Session:\n async def __aenter__(self):\n print("open")\n return self\n\n async def __aexit__(self, exc_type, exc, tb):\n print("close")\n\nasync def main():\n async with Session():\n async for title in titles():\n print(title)\n\nasyncio.run(main())\n```\n:::\n\n:::cell\nAn async generator can `await` before yielding each value. `async for` consumes those values with the asynchronous iteration protocol.\n\n```python\nimport asyncio\n\nasync def titles():\n for slug in ["values", "async-await"]:\n await asyncio.sleep(0)\n yield slug.replace("-", " ").title()\n\nprint(titles.__name__)\n```\n\n```output\ntitles\n```\n:::\n\n:::cell\nAn async context manager defines `__aenter__` and `__aexit__`. `async with` awaits setup and cleanup around the block.\n\n```python\nclass Session:\n async def __aenter__(self):\n print("open")\n return self\n\n async def __aexit__(self, exc_type, exc, tb):\n print("close")\n\nprint(Session.__name__)\n```\n\n```output\nSession\n```\n:::\n\n:::cell\nThe top-level coroutine combines both protocols: open the async resource, then consume the async stream inside it.\n\n```python\nasync def main():\n async with Session():\n async for title in titles():\n print(title)\n\nasyncio.run(main())\n```\n\n```output\nopen\nValues\nAsync Await\nclose\n```\n:::\n\n:::note\n- `async for` consumes asynchronous iterators.\n- `async with` awaits asynchronous setup and cleanup.\n- These forms are common around I/O-shaped resources.\n:::\n', 'attribute-access.md': '+++\nslug = "attribute-access"\ntitle = "Attribute Access"\nsection = "Data Model"\nsummary = "Attribute hooks customize lookup, missing attributes, and assignment."\ndoc_path = "/reference/datamodel.html#customizing-attribute-access"\nsee_also = [\n "properties",\n "descriptors",\n "special-methods",\n "bound-and-unbound-methods",\n]\n+++\n\nAttribute access is usually simple: `obj.name` looks up an attribute. Python exposes hooks for the uncommon cases where lookup or assignment needs to be customized.\n\n`__getattr__` runs only when normal lookup fails, which makes it a safer hook for computed fallback attributes. `__setattr__` runs for every assignment, so it should be used sparingly and carefully.\n\nPrefer ordinary attributes and `@property` first. Reach for these hooks when an object is intentionally adapting another interface, validating all assignments, or exposing dynamic fields.\n\n:::program\n```python\nclass Settings:\n def __init__(self, values):\n self._values = dict(values)\n\n def __getattr__(self, name):\n try:\n return self._values[name]\n except KeyError as error:\n raise AttributeError(name) from error\n\n def __setattr__(self, name, value):\n if name.startswith("_"):\n object.__setattr__(self, name, value)\n else:\n self._values[name] = value\n\nsettings = Settings({"theme": "dark"})\nprint(settings.theme)\nsettings.volume = 7\nprint(settings._values["volume"])\n```\n:::\n\n:::cell\nThe starting point is ordinary: `__init__` stores one real attribute, the `_values` backing dictionary. The hooks in the next cells customize lookup and assignment around it.\n\n```python\nclass Settings:\n def __init__(self, values):\n self._values = dict(values)\n\nsettings = Settings({"theme": "dark"})\nprint(settings._values)\n```\n\n```output\n{\'theme\': \'dark\'}\n```\n:::\n\n:::cell\n`__getattr__` runs only for missing attributes, so it can provide fallback lookup.\n\n```python\nclass Settings:\n def __init__(self, values):\n self._values = dict(values)\n\n def __getattr__(self, name):\n try:\n return self._values[name]\n except KeyError as error:\n raise AttributeError(name) from error\n\nsettings = Settings({"theme": "dark"})\nprint(settings.theme)\n```\n\n```output\ndark\n```\n:::\n\n:::cell\n`__setattr__` intercepts every assignment, including the ones in `__init__`. Underscore names are stored as real attributes through `object.__setattr__`, which avoids recursing through your own hook; public names go to the backing dictionary.\n\n```python\nclass Settings:\n def __init__(self, values):\n self._values = dict(values)\n\n def __setattr__(self, name, value):\n if name.startswith("_"):\n object.__setattr__(self, name, value)\n else:\n self._values[name] = value\n\nsettings = Settings({"theme": "dark"})\nsettings.volume = 7\nprint(settings._values["volume"])\n```\n\n```output\n7\n```\n:::\n\n:::note\n- `__getattr__` is narrower than `__getattribute__` because it handles only missing attributes.\n- `__setattr__` affects every assignment on the instance.\n- Use `property` or descriptors when the behavior is attached to a known attribute name.\n:::\n', 'booleans.md': '+++\nslug = "booleans"\ntitle = "Booleans"\nsection = "Basics"\nsummary = "Booleans represent truth values and combine with logical operators."\ndoc_path = "/library/stdtypes.html#boolean-type-bool"\nsee_also = [\n "truthiness",\n "operators",\n "conditionals",\n]\n+++\n\nBooleans are the values `True` and `False`. They are produced by comparisons and combined with `and`, `or`, and `not`.\n\nPython\'s logical operators short-circuit. That means the right side is evaluated only when needed, which keeps guard checks efficient and safe.\n\nBooleans are also connected to truthiness: many objects can be tested in conditions even when they are not literally `True` or `False`.\n\n:::program\n```python\nlogged_in = True\nhas_permission = False\n\nprint(logged_in and has_permission)\nprint(logged_in or has_permission)\nprint(not has_permission)\n\nname = "Ada"\nprint(name == "Ada" and len(name) > 0)\n\nprint(isinstance(True, int))\nprint(True + True)\nprint(sum([True, True, False, True]))\n\ndef is_strict_int(value):\n return isinstance(value, int) and not isinstance(value, bool)\n\nprint(is_strict_int(True))\nprint(is_strict_int(1))\n```\n:::\n\n:::cell\nUse booleans for facts that are either true or false. Python spells the constants `True` and `False`.\n\nUse `and`, `or`, and `not` to combine truth values. These operators read like English and short-circuit when possible.\n\n```python\nlogged_in = True\nhas_permission = False\n\nprint(logged_in and has_permission)\nprint(logged_in or has_permission)\nprint(not has_permission)\n```\n\n```output\nFalse\nTrue\nTrue\n```\n:::\n\n:::cell\nComparisons produce booleans too, so they compose naturally with logical operators in conditions and validation checks.\n\n```python\nname = "Ada"\nprint(name == "Ada" and len(name) > 0)\n```\n\n```output\nTrue\n```\n:::\n\n:::cell\n`bool` is a subclass of `int`, which is occasionally a footgun. `True` behaves as `1` and `False` as `0` in arithmetic, and `isinstance(True, int)` is `True`. When a function must reject booleans, exclude them explicitly with `isinstance(value, int) and not isinstance(value, bool)`.\n\n```python\nprint(isinstance(True, int))\nprint(True + True)\nprint(sum([True, True, False, True]))\n\ndef is_strict_int(value):\n return isinstance(value, int) and not isinstance(value, bool)\n\nprint(is_strict_int(True))\nprint(is_strict_int(1))\n```\n\n```output\nTrue\n2\n3\nFalse\nTrue\n```\n:::\n\n:::note\n- Boolean constants are `True` and `False`, with capital letters.\n- `and` and `or` short-circuit: Python does not evaluate the right side if the left side already determines the result.\n- Prefer truthiness for containers and explicit comparisons when the exact boolean condition matters.\n- `bool` subclasses `int`; `isinstance(True, int)` is `True`. Exclude booleans explicitly when only "real" integers should pass.\n:::\n', 'bound-and-unbound-methods.md': '+++\nslug = "bound-and-unbound-methods"\ntitle = "Bound and Unbound Methods"\nsection = "Data Model"\nsummary = "instance.method binds self automatically; Class.method is a plain function."\ndoc_path = "/reference/datamodel.html#instance-methods"\nsee_also = [\n "classes",\n "attribute-access",\n "descriptors",\n "callable-objects",\n]\n+++\n\nWhen you write `instance.method`, Python returns a bound method — a callable that already remembers which instance to pass as `self`. When you write `Class.method`, you get the underlying function back, and calling it requires passing an instance yourself.\n\nThat distinction is why methods can be stored in collections, passed as callbacks, and called later without losing track of the object they belong to. Each bound method carries its own `__self__`, so two callables produced from two different instances stay independent even when their underlying function is the same.\n\nThe mechanism is the descriptor protocol: a function attached to a class implements `__get__`, and that hook turns attribute access on an instance into a bound method. The page does not need that detail to use methods, but it explains what is happening underneath.\n\n:::program\n```python\nclass Counter:\n def __init__(self, start=0):\n self.value = start\n\n def increment(self):\n self.value += 1\n return self.value\n\nbound_counter = Counter(10)\nm = bound_counter.increment\nprint(m.__self__ is bound_counter)\nprint(m())\nprint(m())\n\nunbound_counter = Counter(0)\nunbound = Counter.increment\nprint(type(unbound).__name__)\nprint(unbound(unbound_counter))\nprint(unbound(unbound_counter))\n\nhandlers = []\nfor _ in range(2):\n handlers.append(Counter().increment)\n\nprint(handlers[0]())\nprint(handlers[0]())\nprint(handlers[1]())\n\ndescriptor_counter = Counter(0)\nfunc = Counter.__dict__["increment"]\nprint(type(func).__name__)\nrebound = func.__get__(descriptor_counter, Counter)\nprint(type(rebound).__name__)\nprint(rebound.__self__ is descriptor_counter)\n```\n:::\n\n:::cell\n`instance.method` returns a bound method. The method already remembers the instance through `__self__`, so calling it does not require passing `self` again.\n\n```python\nclass Counter:\n def __init__(self, start=0):\n self.value = start\n\n def increment(self):\n self.value += 1\n return self.value\n\nbound_counter = Counter(10)\nm = bound_counter.increment\nprint(m.__self__ is bound_counter)\nprint(m())\nprint(m())\n```\n\n```output\nTrue\n11\n12\n```\n:::\n\n:::cell\n`Class.method` returns the underlying function — there is no `self` attached. Calling it requires passing the instance as the first argument explicitly. Using a fresh counter here makes the output independent of the previous cell.\n\n```python\nunbound_counter = Counter(0)\nunbound = Counter.increment\nprint(type(unbound).__name__)\nprint(unbound(unbound_counter))\nprint(unbound(unbound_counter))\n```\n\n```output\nfunction\n1\n2\n```\n:::\n\n:::cell\nBound methods are first-class values. They can be stored in lists, passed to other functions, and called later. Each bound method carries its own `__self__`, so two methods produced from two different instances stay independent.\n\n```python\nhandlers = []\nfor _ in range(2):\n handlers.append(Counter().increment)\n\nprint(handlers[0]())\nprint(handlers[0]())\nprint(handlers[1]())\n```\n\n```output\n1\n2\n1\n```\n:::\n\n:::cell\nThe binding is the descriptor protocol at work. The function lives on the class as a plain function; instance attribute access invokes `__get__`, which returns a bound method that knows the instance.\n\n```python\ndescriptor_counter = Counter(0)\nfunc = Counter.__dict__["increment"]\nprint(type(func).__name__)\nrebound = func.__get__(descriptor_counter, Counter)\nprint(type(rebound).__name__)\nprint(rebound.__self__ is descriptor_counter)\n```\n\n```output\nfunction\nmethod\nTrue\n```\n:::\n\n:::note\n- `instance.method` produces a bound method whose `__self__` is the instance.\n- `Class.method` produces the plain function and requires you to pass the instance.\n- "Unbound method" is the historical Python 2 term; since Python 3, `Class.method` is simply a function, which is what this page demonstrates.\n- Each bound method is its own object; storing one captures its instance.\n- The binding is implemented by the descriptor protocol on the function object.\n:::\n', 'break-and-continue.md': '+++\nslug = "break-and-continue"\ntitle = "Break and Continue"\nsection = "Control Flow"\nsummary = "break exits a loop early, while continue skips to the next iteration."\ndoc_path = "/tutorial/controlflow.html#break-and-continue-statements"\nsee_also = [\n "for-loops",\n "while-loops",\n "loop-else",\n]\n+++\n\n`break` and `continue` control the nearest enclosing loop. They exist for loops whose body discovers an early stop rule or an item-level skip rule.\n\nUse `continue` when the current item should not run the rest of the body. Use `break` when no later item should be processed.\n\nThe alternative is ordinary `if`/`else` nesting. Prefer `break` and `continue` when they keep the normal path flatter and easier to read.\n\n:::program\n```python\nnames = ["Ada", "", "Grace"]\nfor name in names:\n if not name:\n continue\n print(name)\n\ncommands = ["load", "save", "stop", "delete"]\nfor command in commands:\n if command == "stop":\n break\n print(command)\n```\n:::\n\n:::cell\n`continue` skips the rest of the current iteration. The empty name is ignored, and the loop moves on to the next value.\n\n```python\nnames = ["Ada", "", "Grace"]\nfor name in names:\n if not name:\n continue\n print(name)\n```\n\n```output\nAda\nGrace\n```\n:::\n\n:::cell\n`break` exits the loop immediately. The value after `stop` is never processed because the loop has already ended.\n\n```python\ncommands = ["load", "save", "stop", "delete"]\nfor command in commands:\n if command == "stop":\n break\n print(command)\n```\n\n```output\nload\nsave\n```\n:::\n\n:::note\n- `continue` skips to the next loop iteration.\n- `break` exits the nearest enclosing loop immediately.\n- Prefer plain `if`/`else` when the loop does not need early skip or early stop behavior.\n:::\n', 'bytes-and-bytearray.md': '+++\nslug = "bytes-and-bytearray"\ntitle = "Bytes and Bytearray"\nsection = "Text"\nsummary = "bytes and bytearray store binary data, not Unicode text."\ndoc_path = "/library/stdtypes.html#binary-sequence-types-bytes-bytearray-memoryview"\nsee_also = [\n "strings",\n "literals",\n "networking",\n]\n+++\n\n`str` stores Unicode text. `bytes` stores raw byte values. The boundary matters whenever text leaves Python for a file, network protocol, subprocess, or binary format.\n\nEncoding turns text into bytes with a named encoding such as UTF-8. Decoding turns bytes back into text. The lengths can differ because one Unicode character may require several bytes.\n\nUse immutable `bytes` for stable binary data and `bytearray` when the bytes must be changed in place.\n\n:::program\n```python\ntext = "café"\ndata = text.encode("utf-8")\n\nprint(data)\nprint(len(text), len(data))\nprint(data.decode("utf-8"))\nprint(data[0])\n\npacket = bytearray(b"py")\npacket[0] = ord("P")\nprint(packet)\n```\n:::\n\n:::cell\nEncode text when an external boundary needs bytes. UTF-8 uses one byte for ASCII characters and more than one byte for many other characters.\n\n```python\ntext = "café"\ndata = text.encode("utf-8")\nprint(data)\nprint(len(text), len(data))\n```\n\n```output\nb\'caf\\xc3\\xa9\'\n4 5\n```\n:::\n\n:::cell\nDecode bytes when the program needs text again. The decoder must match the encoding used at the boundary.\n\n```python\nprint(data.decode("utf-8"))\n```\n\n```output\ncafé\n```\n:::\n\n:::cell\nIndexing a `bytes` object returns an integer byte value, not a one-character `bytes` object.\n\n```python\nprint(data[0])\n```\n\n```output\n99\n```\n:::\n\n:::cell\n`bytes` is immutable. Use `bytearray` when binary data must be changed in place.\n\n```python\npacket = bytearray(b"py")\npacket[0] = ord("P")\nprint(packet)\n```\n\n```output\nbytearray(b\'Py\')\n```\n:::\n\n:::note\n- Encode text when an external boundary needs bytes.\n- Decode bytes when you want text again.\n- Indexing `bytes` returns integers from 0 to 255.\n- Use `bytearray` when binary data must be changed in place.\n:::\n', 'callable-objects.md': '+++\nslug = "callable-objects"\ntitle = "Callable Objects"\nsection = "Data Model"\nsummary = "__call__ lets an instance behave like a function while keeping state."\ndoc_path = "/reference/datamodel.html#object.__call__"\nsee_also = [\n "functions",\n "closures",\n "callable-types",\n "bound-and-unbound-methods",\n]\n+++\n\nFunctions are not the only callable objects in Python. Any instance can be called with parentheses when its class defines `__call__`.\n\nCallable objects are useful when behavior needs remembered configuration or evolving state. A closure can do this too; a class is often clearer when the state has multiple fields or needs named methods.\n\nThe tradeoff is ceremony. Use a function for simple behavior, a closure for small captured state, and a callable object when naming the state improves the interface.\n\n:::program\n```python\nclass Multiplier:\n def __init__(self, factor):\n self.factor = factor\n self.calls = 0\n\n def __call__(self, value):\n self.calls += 1\n return value * self.factor\n\ndouble = Multiplier(2)\nprint(double(5))\nprint(double(7))\nprint(double.calls)\n```\n:::\n\n:::cell\nA callable object starts as ordinary state stored on an instance.\n\n```python\nclass Multiplier:\n def __init__(self, factor):\n self.factor = factor\n self.calls = 0\n\ndouble = Multiplier(2)\nprint(double.factor)\n```\n\n```output\n2\n```\n:::\n\n:::cell\n`__call__` makes the instance usable with function-call syntax.\n\n```python\nclass Multiplier:\n def __init__(self, factor):\n self.factor = factor\n self.calls = 0\n\n def __call__(self, value):\n self.calls += 1\n return value * self.factor\n\ndouble = Multiplier(2)\nprint(double(5))\nprint(double(7))\n```\n\n```output\n10\n14\n```\n:::\n\n:::cell\nBecause the callable is still an object, it can remember state across calls.\n\n```python\nprint(double.calls)\n```\n\n```output\n2\n```\n:::\n\n:::note\n- `callable(obj)` checks whether an object can be called.\n- Callable objects are good for named, stateful behavior.\n- Prefer plain functions when no instance state is needed.\n:::\n', 'callable-types.md': '+++\nslug = "callable-types"\ntitle = "Callable Types"\nsection = "Types"\nsummary = "Callable annotations describe functions passed as values."\ndoc_path = "/library/typing.html#annotating-callable-objects"\nsee_also = [\n "functions",\n "callable-objects",\n "protocols",\n]\n+++\n\nCallable annotations describe values that can be called like functions. They are useful when a function accepts a callback, strategy, predicate, or transformation.\n\n`Callable[[int], int]` says how the callback will be called: one integer argument, integer result. The annotation helps tools and readers, while runtime still only needs an object that is actually callable.\n\nUse `Callable` for simple call shapes. Use a protocol when the callback needs named attributes, overloaded signatures, or a more descriptive interface.\n\n:::program\n```python\nfrom collections.abc import Callable\n\n\ndef apply_twice(value: int, func: Callable[[int], int]) -> int:\n return func(func(value))\n\n\ndef add_one(number: int) -> int:\n return number + 1\n\nclass Doubler:\n def __call__(self, number: int) -> int:\n return number * 2\n\nprint(apply_twice(3, add_one))\nprint(apply_twice(3, Doubler()))\nprint(callable(add_one), callable(Doubler()))\n```\n:::\n\n:::cell\nUse `Callable[[Arg], Return]` for function-shaped values. The callback is passed in and called by the receiving function.\n\n```python\nfrom collections.abc import Callable\n\n\ndef apply_twice(value: int, func: Callable[[int], int]) -> int:\n return func(func(value))\n\n\ndef add_one(number: int) -> int:\n return number + 1\n\nprint(apply_twice(3, add_one))\n```\n\n```output\n5\n```\n:::\n\n:::cell\nCallable annotations are structural: an object with `__call__` can also satisfy the shape.\n\n```python\nclass Doubler:\n def __call__(self, number: int) -> int:\n return number * 2\n\nprint(apply_twice(3, Doubler()))\n```\n\n```output\n12\n```\n:::\n\n:::cell\nRuntime callability is a separate question from static annotation. `callable()` checks whether Python can call the object.\n\n```python\nprint(callable(add_one), callable(Doubler()))\n```\n\n```output\nTrue True\n```\n:::\n\n:::note\n- Use `Callable[[Arg], Return]` for simple function-shaped values.\n- The annotation documents how the callback will be called.\n- For complex call signatures, protocols can be clearer.\n:::\n', 'casts-and-any.md': '+++\nslug = "casts-and-any"\ntitle = "Casts and Any"\nsection = "Types"\nsummary = "Any and cast are escape hatches for places static analysis cannot prove."\ndoc_path = "/library/typing.html#typing.cast"\nsee_also = [\n "type-hints",\n "runtime-type-checks",\n "typed-dicts",\n]\n+++\n\n`Any` and `cast()` are escape hatches. They are useful at messy boundaries where a type checker cannot prove what a value is, but they also remove protection when overused.\n\n`Any` tells static tools to stop checking most operations on a value. `cast(T, value)` tells the type checker to treat a value as `T`, but it returns the same runtime object unchanged.\n\nPrefer narrowing with runtime checks when possible. Use `cast()` when another invariant already proves the type and the checker cannot see that proof.\n\n:::program\n```python\nfrom typing import Any, cast\n\nraw: Any = {"score": "98"}\nscore_text = cast(dict[str, str], raw)["score"]\nscore = int(score_text)\n\nprint(score + 2)\nprint(cast(list[int], raw) is raw)\nprint(type(raw).__name__)\n\nvalue: object = {"score": "98"}\nif isinstance(value, dict):\n print(value["score"])\n```\n:::\n\n:::cell\n`Any` disables most static checking for a value. The runtime object is still whatever value was actually assigned.\n\n```python\nfrom typing import Any, cast\n\nraw: Any = {"score": "98"}\nscore_text = cast(dict[str, str], raw)["score"]\nscore = int(score_text)\nprint(score + 2)\n```\n\n```output\n100\n```\n:::\n\n:::cell\n`cast()` does not convert or validate the value. It returns the same object at runtime.\n\n```python\nprint(cast(list[int], raw) is raw)\nprint(type(raw).__name__)\n```\n\n```output\nTrue\ndict\n```\n:::\n\n:::cell\nA real runtime check narrows by inspecting the value. This is safer when the input is untrusted.\n\n```python\nvalue: object = {"score": "98"}\nif isinstance(value, dict):\n print(value["score"])\n```\n\n```output\n98\n```\n:::\n\n:::note\n- `Any` disables most static checking for a value.\n- `cast()` tells the type checker to trust you without changing the runtime object.\n- Prefer narrowing with checks when possible.\n:::\n', 'classes.md': '+++\nslug = "classes"\ntitle = "Classes"\nsection = "Classes"\nsummary = "Classes bundle data and behavior into new object types."\ndoc_path = "/tutorial/classes.html"\nsee_also = [\n "inheritance-and-super",\n "classmethods-and-staticmethods",\n "bound-and-unbound-methods",\n "dataclasses",\n]\n+++\n\nClasses define new object types by bundling data with behavior. They are useful when several values and operations belong together and should travel as one object.\n\nThe alternative is often a dictionary plus separate functions. That is fine for loose data, but a class gives the data a stable API and keeps behavior next to the state it changes.\n\n`__init__` initializes each instance, and methods receive the instance as `self`. Separate instances keep separate state because each object has its own attributes.\n\n:::program\n```python\nclass Counter:\n step = 1\n\n def __init__(self, start=0):\n self.value = start\n\n def increment(self, amount=1):\n self.value += amount\n return self.value\n\nfirst = Counter()\nsecond = Counter(10)\n\nprint(first.value)\nprint(second.value)\nprint(first.increment())\nprint(second.increment(5))\nprint(first.step)\nCounter.step = 5\nprint(second.step)\n\nclass Cart:\n items = []\n\n def add(self, item):\n self.items.append(item)\n\nshared_a = Cart()\nshared_b = Cart()\nshared_a.add("apple")\nprint(shared_b.items)\n\nclass FixedCart:\n def __init__(self):\n self.items = []\n\n def add(self, item):\n self.items.append(item)\n\nown_a = FixedCart()\nown_b = FixedCart()\nown_a.add("apple")\nprint(own_b.items)\n```\n:::\n\n:::cell\nDefine a class when data and behavior should travel together. The initializer gives each object its starting state.\n\n```python\nclass Counter:\n def __init__(self, start=0):\n self.value = start\n\nfirst = Counter()\nsecond = Counter(10)\nprint(first.value)\nprint(second.value)\n```\n\n```output\n0\n10\n```\n:::\n\n:::cell\nMethods are functions attached to the class. `self` is the particular object receiving the method call, so separate instances keep separate state.\n\n```python\nclass Counter:\n def __init__(self, start=0):\n self.value = start\n\n def increment(self, amount=1):\n self.value += amount\n return self.value\n\nfirst = Counter()\nsecond = Counter(10)\nprint(first.increment())\nprint(second.increment(5))\n```\n\n```output\n1\n15\n```\n:::\n\n:::cell\nA name defined directly on the class body is a class attribute, shared by every instance. Reading falls back to the class when the instance has no attribute of that name; assigning to the class itself changes the value for every instance at once.\n\n```python\nclass Counter:\n step = 1\n\n def __init__(self, start=0):\n self.value = start\n\nfirst = Counter()\nsecond = Counter()\nprint(first.step)\nCounter.step = 5\nprint(second.step)\n```\n\n```output\n1\n5\n```\n:::\n\n:::cell\nA mutable class attribute is shared mutable state — the classic footgun. Define per-instance containers in `__init__` so each object owns its own copy.\n\n```python\nclass Cart:\n items = []\n\n def add(self, item):\n self.items.append(item)\n\nshared_a = Cart()\nshared_b = Cart()\nshared_a.add("apple")\nprint(shared_b.items)\n\nclass FixedCart:\n def __init__(self):\n self.items = []\n\n def add(self, item):\n self.items.append(item)\n\nown_a = FixedCart()\nown_b = FixedCart()\nown_a.add("apple")\nprint(own_b.items)\n```\n\n```output\n[\'apple\']\n[]\n```\n:::\n\n:::note\n- `self` is the instance the method is operating on.\n- `__init__` initializes each new object.\n- Class attributes are shared across instances; instance attributes belong to one object.\n- Put mutable defaults in `__init__`, not on the class body.\n- Use classes when behavior belongs with state; use dictionaries for looser structured data.\n:::\n', 'classmethods-and-staticmethods.md': '+++\nslug = "classmethods-and-staticmethods"\ntitle = "Classmethods and Staticmethods"\nsection = "Classes"\nsummary = "Three method shapes: instance, class, and static — each receives a different first argument."\ndoc_path = "/library/functions.html#classmethod"\nsee_also = [\n "classes",\n "decorators",\n "inheritance-and-super",\n]\n+++\n\nA regular method receives the instance as `self`. `@classmethod` makes a method receive the class as `cls` instead, which is the standard shape for alternate constructors. `@staticmethod` removes the implicit first argument entirely, leaving a plain function attached to the class for namespacing.\n\nThe pressure that justifies the decorators is name organization. `Date.from_string("2026-05-09")` reads better than a free-floating `parse_date` function, and `Date.is_leap_year(2024)` keeps the helper next to the class it belongs to even when the helper does not need any class state.\n\nPick instance methods when the work depends on instance state, classmethods when an alternate constructor or class-level operation is the right shape, and staticmethods when the function only happens to live near a class.\n\n:::program\n```python\nclass Date:\n def __init__(self, year, month, day):\n self.year = year\n self.month = month\n self.day = day\n\n def display(self):\n return f"{self.year}-{self.month:02d}-{self.day:02d}"\n\n @classmethod\n def from_string(cls, text):\n year, month, day = (int(part) for part in text.split("-"))\n return cls(year, month, day)\n\n @staticmethod\n def is_leap_year(year):\n return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)\n\ntoday = Date(2026, 5, 9)\nprint(today.display())\n\nlater = Date.from_string("2026-12-31")\nprint(later.display())\n\nprint(Date.is_leap_year(2024))\nprint(Date.is_leap_year(2025))\n\nclass Demo:\n def instance_method(self):\n return type(self).__name__\n\n @classmethod\n def class_method(cls):\n return cls.__name__\n\n @staticmethod\n def static_method():\n return "no receiver"\n\nprint(Demo().instance_method())\nprint(Demo.class_method())\nprint(Demo.static_method())\n```\n:::\n\n:::cell\nAn instance method receives the instance as `self` and reads its state. This is the default and the right shape when the work depends on a particular object\'s data.\n\n```python\nclass Date:\n def __init__(self, year, month, day):\n self.year = year\n self.month = month\n self.day = day\n\n def display(self):\n return f"{self.year}-{self.month:02d}-{self.day:02d}"\n\ntoday = Date(2026, 5, 9)\nprint(today.display())\n```\n\n```output\n2026-05-09\n```\n:::\n\n:::cell\n`@classmethod` makes the method receive the class itself as `cls`. The canonical use is an alternate constructor that parses some other input format and calls `cls(...)`. Because `cls` is the actual class, subclasses calling the same method get an instance of their own type.\n\n```python\nclass Date:\n def __init__(self, year, month, day):\n self.year = year\n self.month = month\n self.day = day\n\n @classmethod\n def from_string(cls, text):\n year, month, day = (int(part) for part in text.split("-"))\n return cls(year, month, day)\n\nlater = Date.from_string("2026-12-31")\nprint(later.year, later.month, later.day)\n```\n\n```output\n2026 12 31\n```\n:::\n\n:::cell\n`@staticmethod` strips the implicit first argument. The function lives on the class for namespacing — like `Date.is_leap_year(2024)` — but does not touch any instance or class state.\n\n```python\nclass Date:\n @staticmethod\n def is_leap_year(year):\n return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)\n\nprint(Date.is_leap_year(2024))\nprint(Date.is_leap_year(2025))\n```\n\n```output\nTrue\nFalse\n```\n:::\n\n:::cell\nSide by side: instance methods receive the instance, classmethods receive the class, staticmethods receive nothing. Classmethods and staticmethods can be called on either the class or an instance.\n\n```python\nclass Demo:\n def instance_method(self):\n return type(self).__name__\n\n @classmethod\n def class_method(cls):\n return cls.__name__\n\n @staticmethod\n def static_method():\n return "no receiver"\n\nprint(Demo().instance_method())\nprint(Demo.class_method())\nprint(Demo.static_method())\n```\n\n```output\nDemo\nDemo\nno receiver\n```\n:::\n\n:::note\n- Instance methods need an instance; classmethods and staticmethods can be called on the class.\n- Use `@classmethod` for alternate constructors and class-level operations that respect subclassing.\n- Use `@staticmethod` only when a function is truly independent of instance and class state but still belongs in the class\'s namespace.\n- A free function is often the right answer when neither decorator applies.\n:::\n', 'closures.md': '+++\nslug = "closures"\ntitle = "Closures"\nsection = "Functions"\nsummary = "Inner functions can remember values from an enclosing scope."\ndoc_path = "/reference/executionmodel.html#binding-of-names"\nsee_also = [\n "functions",\n "lambdas",\n "decorators",\n "partial-functions",\n]\n+++\n\nA closure is a function that remembers names from the scope where it was created. This lets you configure behavior once and call it later.\n\nEach call to the outer function creates a separate remembered environment. That is why `double` and `triple` can share the same code but keep different factors.\n\nClosures are a foundation for decorators, callbacks, and small function factories.\n\n:::program\n```python\ndef make_multiplier(factor):\n def multiply(value):\n return value * factor\n return multiply\n\ndouble = make_multiplier(2)\nprint(double(5))\n\ntriple = make_multiplier(3)\nprint(triple(5))\n\nlate = []\nfor i in range(3):\n late.append(lambda: i)\nprint([f() for f in late])\n\nbound = []\nfor i in range(3):\n bound.append(lambda i=i: i)\nprint([f() for f in bound])\n```\n:::\n\n:::cell\nDefine a function inside another function when the inner behavior needs to remember setup from the outer call. The returned function keeps access to `factor`.\n\n```python\ndef make_multiplier(factor):\n def multiply(value):\n return value * factor\n return multiply\n\ndouble = make_multiplier(2)\nprint(double(5))\n```\n\n```output\n10\n```\n:::\n\n:::cell\nCalling the outer function again creates a separate closure. `triple` uses the same inner code, but remembers a different `factor`.\n\n```python\ntriple = make_multiplier(3)\nprint(triple(5))\n```\n\n```output\n15\n```\n:::\n\n:::cell\nClosures bind names, not values. Lambdas defined in a loop all reference the same loop variable, so calling them later sees its final value. Capture the value at definition time by binding it as a default argument — `lambda i=i: i` — so each closure remembers its own `i`.\n\n```python\nlate = []\nfor i in range(3):\n late.append(lambda: i)\nprint([f() for f in late])\n\nbound = []\nfor i in range(3):\n bound.append(lambda i=i: i)\nprint([f() for f in bound])\n```\n\n```output\n[2, 2, 2]\n[0, 1, 2]\n```\n:::\n\n:::note\n- A closure keeps access to names from the scope where the inner function was created.\n- Each call to the outer function can create a separate remembered environment.\n- Closures are useful for callbacks, small factories, and decorators.\n- Closures bind names, not values; capture loop variables with `lambda x=x: ...` to freeze them at definition time.\n:::\n', 'collections-module.md': '+++\nslug = "collections-module"\ntitle = "Collections Module"\nsection = "Collections"\nsummary = "collections provides specialized containers for common data shapes."\ndoc_path = "/library/collections.html"\nsee_also = [\n "dicts",\n "lists",\n "tuples",\n "sets",\n]\n+++\n\n`collections` provides specialized containers for common shapes that would otherwise require repetitive plumbing. Use it when the shape has a name: counting, grouping, queueing, or lightweight records.\n\nThese types are not replacements for `list`, `dict`, `tuple`, and `set`. They are small standard-library tools for cases where an ordinary container would hide the intent behind manual bookkeeping.\n\nThe examples below map each type to the question it answers.\n\n:::program\n```python\nfrom collections import Counter, defaultdict, deque, namedtuple\n\ncounts = Counter("banana")\nprint(counts.most_common(2))\n\ngroups = defaultdict(list)\nfor name, team in [("Ada", "red"), ("Grace", "blue"), ("Lin", "red")]:\n groups[team].append(name)\nprint(dict(groups))\n\nqueue = deque(["first"])\nqueue.append("second")\nprint(queue.popleft())\n\nPoint = namedtuple("Point", "x y")\nprint(Point(2, 3).x)\n```\n:::\n\n:::cell\nUse `Counter` when the question is "how many times did each value appear?"\n\n```python\nfrom collections import Counter\n\ncounts = Counter("banana")\nprint(counts.most_common(2))\n```\n\n```output\n[(\'a\', 3), (\'n\', 2)]\n```\n:::\n\n:::cell\nUse `defaultdict(list)` when each key gathers multiple values and the missing-key case should create an empty list automatically.\n\n```python\nfrom collections import defaultdict\n\ngroups = defaultdict(list)\nfor name, team in [("Ada", "red"), ("Grace", "blue"), ("Lin", "red")]:\n groups[team].append(name)\nprint(dict(groups))\n```\n\n```output\n{\'red\': [\'Ada\', \'Lin\'], \'blue\': [\'Grace\']}\n```\n:::\n\n:::cell\nUse `deque` for queue operations at both ends, and `namedtuple` when a tiny immutable record needs names as well as positions.\n\n```python\nfrom collections import deque, namedtuple\n\nqueue = deque(["first"])\nqueue.append("second")\nprint(queue.popleft())\n\nPoint = namedtuple("Point", "x y")\nprint(Point(2, 3).x)\n```\n\n```output\nfirst\n2\n```\n:::\n\n:::note\n- `Counter` counts, `defaultdict` groups, `deque` queues, and `namedtuple` names record fields.\n- Prefer the built-in containers until a specialized shape makes the code clearer.\n- For new structured records with defaults and methods, consider `dataclasses` instead of `namedtuple`.\n:::\n', 'comprehension-patterns.md': '+++\nslug = "comprehension-patterns"\ntitle = "Comprehension Patterns"\nsection = "Collections"\nsummary = "Comprehensions can use multiple for clauses and filters when the shape stays clear."\ndoc_path = "/tutorial/datastructures.html#list-comprehensions"\nsee_also = [\n "comprehensions",\n "generator-expressions",\n "for-loops",\n]\n+++\n\nComprehensions can contain more than one `for` clause and more than one `if` filter. The clauses are read in the same order as nested loops.\n\nUse these forms only while the shape remains easy to scan. If a comprehension starts needing several names, comments, or branches, an explicit loop is usually better.\n\nNested comprehensions build concrete collections immediately, just like simpler list, dict, and set comprehensions.\n\n:::program\n```python\ncolors = ["red", "blue"]\nsizes = ["S", "M"]\nvariants = [(color, size) for color in colors for size in sizes]\nprint(variants)\n\nnumbers = range(10)\nfiltered = [n for n in numbers if n % 2 == 0 if n > 2]\nprint(filtered)\n```\n:::\n\n:::cell\nMultiple `for` clauses behave like nested loops. The leftmost `for` is the outer loop, and the next `for` runs inside it.\n\n```python\ncolors = ["red", "blue"]\nsizes = ["S", "M"]\nvariants = [(color, size) for color in colors for size in sizes]\nprint(variants)\n```\n\n```output\n[(\'red\', \'S\'), (\'red\', \'M\'), (\'blue\', \'S\'), (\'blue\', \'M\')]\n```\n:::\n\n:::cell\nMultiple `if` clauses filter values. They are useful for simple conditions, but an explicit loop is clearer when the rules need names or explanation.\n\n```python\nnumbers = range(10)\nfiltered = [n for n in numbers if n % 2 == 0 if n > 2]\nprint(filtered)\n```\n\n```output\n[4, 6, 8]\n```\n:::\n\n:::note\n- Read comprehension clauses from left to right.\n- Multiple `for` clauses act like nested loops.\n- Prefer an explicit loop when the comprehension stops being obvious.\n:::\n', 'comprehensions.md': '+++\nslug = "comprehensions"\ntitle = "Comprehensions"\nsection = "Collections"\nsummary = "Comprehensions build collections by mapping and filtering iterables."\ndoc_path = "/tutorial/datastructures.html#list-comprehensions"\nsee_also = [\n "for-loops",\n "generator-expressions",\n "comprehension-patterns",\n "lists",\n]\n+++\n\nComprehensions are expression forms for building concrete collections from iterables. Read them from left to right: produce this value, for each item, optionally only when a condition is true.\n\nThey are best for direct transformations where the expression is still easy to scan. When the work needs several statements or names, an explicit loop is usually clearer.\n\nList, dictionary, and set comprehensions are eager: they build collections immediately. Generator expressions use similar syntax to stream values later and are covered in the Iteration section.\n\n:::program\n```python\nnames = ["ada", "guido", "grace"]\ntitled = [name.title() for name in names]\nprint(titled)\n\nscores = {"Ada": 10, "Guido": 8, "Grace": 10}\nhigh_scores = {name: score for name, score in scores.items() if score >= 10}\nprint(high_scores)\n\nunique_scores = {score for score in scores.values()}\nprint(sorted(unique_scores))\n```\n:::\n\n:::cell\nA list comprehension maps each input item to one output item. This one calls `title()` for every name and collects the results in a new list.\n\n```python\nnames = ["ada", "guido", "grace"]\ntitled = [name.title() for name in names]\nprint(titled)\n```\n\n```output\n[\'Ada\', \'Guido\', \'Grace\']\n```\n:::\n\n:::cell\nAdd an `if` clause when only some items should appear. A dictionary comprehension can transform key/value pairs while preserving the dictionary shape.\n\n```python\nscores = {"Ada": 10, "Guido": 8, "Grace": 10}\nhigh_scores = {name: score for name, score in scores.items() if score >= 10}\nprint(high_scores)\n```\n\n```output\n{\'Ada\': 10, \'Grace\': 10}\n```\n:::\n\n:::cell\nA set comprehension keeps only unique results. Here two people have the same score, so the resulting set has two values — printed through `sorted()` because sets have no display order to rely on.\n\n```python\nunique_scores = {score for score in scores.values()}\nprint(sorted(unique_scores))\n```\n\n```output\n[8, 10]\n```\n:::\n\n:::note\n- The left side says what to produce; the `for` clause says where values come from.\n- Use an `if` clause for simple filters.\n- List, dict, and set comprehensions build concrete collections immediately.\n- Switch to a loop when the transformation needs multiple steps or explanations.\n:::\n', 'conditionals.md': '+++\nslug = "conditionals"\ntitle = "Conditionals"\nsection = "Control Flow"\nsummary = "if, elif, and else choose which block runs."\ndoc_path = "/tutorial/controlflow.html#if-statements"\nsee_also = [\n "booleans",\n "truthiness",\n "guard-clauses",\n "match-statements",\n]\n+++\n\n`if`, `elif`, and `else` let a program choose one path based on a condition. Python uses indentation to show which statements belong to each branch.\n\nConditions use Python truthiness: booleans work directly, and many objects such as empty lists or empty strings are considered false. Order branches from most specific to most general.\n\nUse `elif` to keep one decision flat instead of nested. Use Python\'s ternary expression only when you are choosing between two values.\n\n:::program\n```python\ntemperature = 72\n\nif temperature < 60:\n print("cold")\nelif temperature < 80:\n print("comfortable")\nelse:\n print("hot")\n\nitems = ["coat", "hat"]\nif items:\n print(f"packing {len(items)} items")\n\nstatus = "ok" if temperature < 90 else "danger"\nprint(status)\n```\n:::\n\n:::cell\nStart with the value that the branches will test. A conditional is only useful when the branch condition is visible and meaningful.\n\nUse `if`, `elif`, and `else` for one ordered choice. Python tests the branches from top to bottom and runs only the first matching block.\n\n```python\ntemperature = 72\n\nif temperature < 60:\n print("cold")\nelif temperature < 80:\n print("comfortable")\nelse:\n print("hot")\n```\n\n```output\ncomfortable\n```\n:::\n\n:::cell\nTruthiness is part of conditional flow. Empty collections are false, so `if items:` reads as “if there is anything to work with.”\n\n```python\nitems = ["coat", "hat"]\nif items:\n print(f"packing {len(items)} items")\n```\n\n```output\npacking 2 items\n```\n:::\n\n:::cell\nUse the ternary expression when you are choosing a value. If either side needs multiple statements, use a normal `if` block instead.\n\n```python\nstatus = "ok" if temperature < 90 else "danger"\nprint(status)\n```\n\n```output\nok\n```\n:::\n\n:::note\n- Python has no mandatory parentheses around conditions; the colon and indentation define the block.\n- Comparison operators such as `<` and `==` can be chained, as in `0 < value < 10`.\n- Keep branch bodies short; move larger work into functions so the decision remains easy to scan.\n:::\n', 'constants.md': '+++\nslug = "constants"\ntitle = "Constants"\nsection = "Basics"\nsummary = "Python uses naming conventions and optional types for values that should not change."\ndoc_path = "/library/typing.html#typing.Final"\nsee_also = [\n "variables",\n "literal-and-final",\n "type-hints",\n]\n+++\n\nPython has no `const` keyword for ordinary names. Modules use all-caps names such as `MAX_RETRIES` to say “treat this as fixed configuration, not changing state.”\n\nThe interpreter will still let code rebind the name. That is why constants are primarily an API and readability convention. If a project also uses static typing, `Final` can make the convention machine-checkable.\n\nNamed constants remove magic values from code and give repeated literals one place to change.\n\n:::program\n```python\nfrom typing import Final\n\nMAX_RETRIES: Final = 3\nAPI_VERSION = "2026-05"\n\nfor attempt in range(1, MAX_RETRIES + 1):\n print(f"attempt {attempt} of {MAX_RETRIES}")\n\nprint(API_VERSION)\n\nMAX_RETRIES = 5\nprint(MAX_RETRIES)\n```\n:::\n\n:::cell\nAll-caps names communicate design intent: this value is configuration that callers should treat as fixed.\n\n```python\nMAX_RETRIES = 3\n\nfor attempt in range(1, MAX_RETRIES + 1):\n print(f"attempt {attempt} of {MAX_RETRIES}")\n```\n\n```output\nattempt 1 of 3\nattempt 2 of 3\nattempt 3 of 3\n```\n:::\n\n:::cell\nConstants are useful when a repeated literal deserves a name at the domain boundary.\n\n```python\nAPI_VERSION = "2026-05"\nprint(API_VERSION)\n```\n\n```output\n2026-05\n```\n:::\n\n:::cell\n`Final` lets type checkers reject reassignment, but Python still runs ordinary rebinding at runtime.\n\n```python\nfrom typing import Final\n\nMAX_RETRIES: Final = 3\nMAX_RETRIES = 5\nprint(MAX_RETRIES)\n```\n\n```output\n5\n```\n:::\n\n:::note\n- Python constants are a convention, not a runtime lock.\n- Use all-caps names for fixed module-level configuration.\n- Add `Final` when static tooling should flag accidental rebinding.\n:::\n', 'container-protocols.md': '+++\nslug = "container-protocols"\ntitle = "Container Protocols"\nsection = "Data Model"\nsummary = "Container methods connect objects to indexing, membership, and item assignment."\ndoc_path = "/reference/datamodel.html#emulating-container-types"\nsee_also = [\n "lists",\n "dicts",\n "special-methods",\n]\n+++\n\nContainer protocols let a class behave like the collection it represents. Instead of inventing method names such as `has()` or `lookup()`, the object can support `in`, indexing, and assignment.\n\nThe key methods are small and familiar: `__contains__` powers `in`, `__getitem__` powers `obj[key]`, and `__setitem__` powers `obj[key] = value`. Add only the operations the object can honestly support.\n\nThis keeps the public interface aligned with Python\'s built-in containers. Callers can use the same syntax for custom records, caches, tables, and sequence-like objects.\n\n:::program\n```python\nclass Scores:\n def __init__(self):\n self._scores = {}\n\n def __contains__(self, name):\n return name in self._scores\n\n def __getitem__(self, name):\n return self._scores[name]\n\n def __setitem__(self, name, score):\n self._scores[name] = score\n\nscores = Scores()\nscores["Ada"] = 98\nprint("Ada" in scores)\nprint(scores["Ada"])\n```\n:::\n\n:::cell\n`__setitem__` gives assignment syntax to a custom container.\n\n```python\nclass Scores:\n def __init__(self):\n self._scores = {}\n\n def __setitem__(self, name, score):\n self._scores[name] = score\n\nscores = Scores()\nscores["Ada"] = 98\nprint(scores._scores)\n```\n\n```output\n{\'Ada\': 98}\n```\n:::\n\n:::cell\n`__contains__` answers membership tests written with `in`.\n\n```python\nclass Scores:\n def __init__(self):\n self._scores = {"Ada": 98}\n\n def __contains__(self, name):\n return name in self._scores\n\nscores = Scores()\nprint("Ada" in scores)\n```\n\n```output\nTrue\n```\n:::\n\n:::cell\n`__getitem__` connects bracket lookup to your internal storage.\n\n```python\nclass Scores:\n def __init__(self):\n self._scores = {"Ada": 98}\n\n def __getitem__(self, name):\n return self._scores[name]\n\nscores = Scores()\nprint(scores["Ada"])\n```\n\n```output\n98\n```\n:::\n\n:::note\n- Implement the narrowest container protocol your object needs.\n- Use `KeyError` and `IndexError` consistently with built-in containers.\n- If a plain `dict` or `list` is enough, prefer it over a custom container.\n:::\n', 'context-managers.md': '+++\nslug = "context-managers"\ntitle = "Context Managers"\nsection = "Data Model"\nsummary = "with ensures setup and cleanup happen together."\ndoc_path = "/reference/datamodel.html#context-managers"\nsee_also = [\n "exceptions",\n "special-methods",\n "descriptors",\n]\n+++\n\nContext managers define setup and cleanup around a block of code. The `with` statement guarantees that cleanup runs when the block exits, even when an exception is raised.\n\nThe protocol is powered by `__enter__` and `__exit__`. The `contextlib.contextmanager` decorator is a concise way to write the same idea as a generator when a full class would be noisy.\n\nProduction code often uses `with` for files, locks, transactions, temporary state, and resources that need reliable release.\n\n:::program\n```python\nfrom contextlib import contextmanager\n\nclass Tag:\n def __init__(self, name):\n self.name = name\n\n def __enter__(self):\n print(f"<{self.name}>")\n return self\n\n def __exit__(self, exc_type, exc, tb):\n print(f"</{self.name}>")\n return False\n\n@contextmanager\ndef tag(name):\n print(f"<{name}>")\n try:\n yield\n finally:\n print(f"</{name}>")\n\nwith Tag("section"):\n print("content")\n\ntry:\n with tag("error"):\n raise ValueError("boom")\nexcept ValueError:\n print("handled")\n```\n:::\n\n:::cell\nA class-based context manager implements `__enter__` and `__exit__`. The value returned by `__enter__` is bound by `as` when the `with` statement uses it.\n\n```python\nclass Tag:\n def __init__(self, name):\n self.name = name\n\n def __enter__(self):\n print(f"<{self.name}>")\n return self\n\n def __exit__(self, exc_type, exc, tb):\n print(f"</{self.name}>")\n return False\n\nwith Tag("section"):\n print("content")\n```\n\n```output\n<section>\ncontent\n</section>\n```\n:::\n\n:::cell\n`contextlib.contextmanager` writes the same setup/cleanup shape as a generator. Code before `yield` is setup, and code after `yield` is cleanup.\n\n```python\nfrom contextlib import contextmanager\n\n@contextmanager\ndef tag(name):\n print(f"<{name}>")\n try:\n yield\n finally:\n print(f"</{name}>")\n\nwith tag("note"):\n print("body")\n```\n\n```output\n<note>\nbody\n</note>\n```\n:::\n\n:::cell\nCleanup still runs when the block raises. Returning `False` from `__exit__`, or letting a generator context manager re-raise, allows the exception to keep propagating.\n\n```python\ntry:\n with tag("error"):\n raise ValueError("boom")\nexcept ValueError:\n print("handled")\n```\n\n```output\n<error>\n</error>\nhandled\n```\n:::\n\n:::note\n- Files, locks, and temporary state commonly use context managers.\n- `__enter__` and `__exit__` power the protocol.\n- Use `finally` when cleanup must happen after errors too.\n- Returning true from `__exit__` suppresses an exception; do that only intentionally.\n:::\n', 'copying-collections.md': '+++\nslug = "copying-collections"\ntitle = "Copying Collections"\nsection = "Collections"\nsummary = "Copies can duplicate the outer container while nested objects may still be shared."\ndoc_path = "/library/copy.html"\nsee_also = [\n "mutability",\n "lists",\n "dicts",\n]\n+++\n\nCopying answers two different questions: do you need a new outer container, or do you also need independent nested objects? A plain assignment gives another name for the same object. A shallow copy duplicates only the outer container. `copy.deepcopy()` recursively copies contained objects.\n\nMost Python code wants a shallow copy or a deliberate rebuild. Use a deep copy only when shared nested state would be wrong and the objects involved are safe to duplicate.\n\nThe outputs below show the footgun directly: a shallow copy has a different outer list, but its inner lists are still the same objects.\n\n:::program\n```python\nimport copy\n\nrows = [["Ada"], ["Grace"]]\nalias = rows\nshallow = rows.copy()\ndeep = copy.deepcopy(rows)\n\nrows[0].append("Lovelace")\n\nprint(alias is rows)\nprint(shallow is rows)\nprint(rows[0] is shallow[0])\nprint(rows[0] is deep[0])\nprint(shallow)\nprint(deep)\n```\n:::\n\n:::cell\nAssignment does not copy a collection. It gives the same list another name.\n\n```python\nrows = [["Ada"], ["Grace"]]\nalias = rows\n\nprint(alias is rows)\n```\n\n```output\nTrue\n```\n:::\n\n:::cell\nA shallow copy creates a new outer list, but nested lists are still shared.\n\n```python\nshallow = rows.copy()\nrows[0].append("Lovelace")\n\nprint(shallow is rows)\nprint(rows[0] is shallow[0])\nprint(shallow)\n```\n\n```output\nFalse\nTrue\n[[\'Ada\', \'Lovelace\'], [\'Grace\']]\n```\n:::\n\n:::cell\nA deep copy is independent at the nested level, so later mutation of `rows[0]` does not appear in `deep`.\n\n```python\nimport copy\n\nrows = [["Ada"], ["Grace"]]\ndeep = copy.deepcopy(rows)\nrows[0].append("Lovelace")\n\nprint(rows[0] is deep[0])\nprint(deep)\n```\n\n```output\nFalse\n[[\'Ada\'], [\'Grace\']]\n```\n:::\n\n:::note\n- Assignment aliases; it does not copy.\n- Shallow copies duplicate the outer container only.\n- Deep copies are useful for nested independence, but they can be expensive and surprising for objects with external resources.\n:::\n', 'csv-data.md': '+++\nslug = "csv-data"\ntitle = "CSV Data"\nsection = "Standard Library"\nsummary = "csv reads and writes row-shaped text data."\ndoc_path = "/library/csv.html"\nsee_also = [\n "strings",\n "dicts",\n "json",\n]\n+++\n\nCSV is row-shaped text: each line is a record, and each comma-separated field arrives as a string. The `csv` module understands quoting, delimiters, and newlines, so it is safer than splitting lines by comma yourself.\n\nUse `DictReader` when a header row names the columns. Convert fields explicitly after reading, and use `DictWriter` when the program needs to produce the same row shape again.\n\nCSV is a good fit for flat tabular data. Use JSON or another structured format when values are nested or when types need to survive the text boundary.\n\n:::program\n```python\nimport csv\nimport io\n\ntext = "name,score\\nAda,98\\nGrace,95\\n"\nrows = list(csv.DictReader(io.StringIO(text)))\nprint(rows[0])\nprint(sum(int(row["score"]) for row in rows))\n\noutput = io.StringIO(newline="")\nwriter = csv.DictWriter(output, fieldnames=["name", "passed"])\nwriter.writeheader()\nwriter.writerow({"name": "Ada", "passed": True})\nprint(output.getvalue().splitlines()[1])\n```\n:::\n\n:::cell\n`DictReader` uses the header row as dictionary keys. The values are still strings because CSV is text.\n\n```python\nimport csv\nimport io\n\ntext = "name,score\\nAda,98\\nGrace,95\\n"\nrows = list(csv.DictReader(io.StringIO(text)))\n\nprint(rows[0])\nprint(type(rows[0]["score"]).__name__)\n```\n\n```output\n{\'name\': \'Ada\', \'score\': \'98\'}\nstr\n```\n:::\n\n:::cell\nConvert numeric fields at the boundary where the program leaves CSV text and starts doing arithmetic.\n\n```python\nprint(sum(int(row["score"]) for row in rows))\n```\n\n```output\n193\n```\n:::\n\n:::cell\n`DictWriter` turns dictionaries back into row-shaped text with the same column order.\n\n```python\noutput = io.StringIO(newline="")\nwriter = csv.DictWriter(output, fieldnames=["name", "passed"])\nwriter.writeheader()\nwriter.writerow({"name": "Ada", "passed": True})\n\nprint(output.getvalue().splitlines()[1])\n```\n\n```output\nAda,True\n```\n:::\n\n:::note\n- Let `csv` handle quoting and delimiters instead of calling `split(",")`.\n- CSV fields are text until your code converts them.\n- Reach for JSON when records need nested lists, dictionaries, booleans, or numbers that preserve their type.\n:::\n', 'custom-exceptions.md': '+++\nslug = "custom-exceptions"\ntitle = "Custom Exceptions"\nsection = "Errors"\nsummary = "Custom exception classes name failures that belong to your domain."\ndoc_path = "/tutorial/errors.html#user-defined-exceptions"\nsee_also = [\n "exceptions",\n "exception-chaining",\n "warnings",\n "logging",\n]\n+++\n\nCustom exceptions give names to failures in your problem domain. A named exception is easier to catch and explain than a generic error with only a string message.\n\nRaise the custom exception at the point where the invalid state is discovered. Include a message for the specific occurrence.\n\nCatch custom exceptions at the boundary where recovery makes sense, such as returning an error response or asking for corrected input.\n\n:::program\n```python\nclass EmptyCartError(Exception):\n pass\n\nprint(EmptyCartError.__name__)\n\n\ndef checkout(items):\n if not items:\n raise EmptyCartError("cart is empty")\n return "paid"\n\nprint(checkout(["book"]))\n\ntry:\n checkout([])\nexcept EmptyCartError as error:\n print(error)\n```\n:::\n\n:::cell\nCreate a custom exception when a failure has a name in your problem domain. The class can be empty at first.\n\n```python\nclass EmptyCartError(Exception):\n pass\n\nprint(EmptyCartError.__name__)\n```\n\n```output\nEmptyCartError\n```\n:::\n\n:::cell\nRaise the custom exception where the invalid state is detected. Normal inputs still follow the ordinary success path.\n\n```python\ndef checkout(items):\n if not items:\n raise EmptyCartError("cart is empty")\n return "paid"\n\nprint(checkout(["book"]))\n```\n\n```output\npaid\n```\n:::\n\n:::cell\nCallers can catch the precise error type without accidentally catching unrelated failures.\n\n```python\ntry:\n checkout([])\nexcept EmptyCartError as error:\n print(error)\n```\n\n```output\ncart is empty\n```\n:::\n\n:::note\n- Subclass `Exception` for errors callers are expected to catch.\n- A custom exception name can be clearer than reusing a generic `ValueError` everywhere.\n- Catch custom exceptions at a boundary that can recover or report clearly.\n:::\n', 'dataclasses.md': '+++\nslug = "dataclasses"\ntitle = "Dataclasses"\nsection = "Classes"\nsummary = "dataclass generates common class methods for data containers."\ndoc_path = "/library/dataclasses.html"\nsee_also = [\n "structured-data-shapes",\n "classes",\n "type-hints",\n]\n+++\n\n`dataclass` is a standard-library decorator for classes that mainly store data. It generates methods such as `__init__` and `__repr__` from type-annotated fields.\n\nDataclasses reduce boilerplate while keeping classes explicit. They are a good fit for simple records, configuration objects, and values passed between layers.\n\nType annotations define fields. Defaults work like normal class attributes and appear in the generated initializer.\n\n:::program\n```python\nfrom dataclasses import dataclass\n\n@dataclass\nclass User:\n name: str\n active: bool = True\n\nuser = User("Ada")\nprint(user)\nprint(user.name)\n\ninactive = User("Guido", active=False)\nprint(inactive)\nprint(inactive.active)\n```\n:::\n\n:::cell\nA dataclass uses annotations to define fields. Python generates an initializer, so the class can be constructed without writing `__init__` by hand.\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass\nclass User:\n name: str\n active: bool = True\n\nuser = User("Ada")\nprint(user)\n```\n\n```output\nUser(name=\'Ada\', active=True)\n```\n:::\n\n:::cell\nThe generated instance still exposes ordinary attributes. A dataclass is a regular class with useful methods filled in.\n\n```python\nprint(user.name)\n```\n\n```output\nAda\n```\n:::\n\n:::cell\nDefaults can be overridden by keyword. The generated representation includes the field names, which is useful during debugging.\n\n```python\ninactive = User("Guido", active=False)\nprint(inactive)\nprint(inactive.active)\n```\n\n```output\nUser(name=\'Guido\', active=False)\nFalse\n```\n:::\n\n:::note\n- Type annotations define dataclass fields.\n- Dataclasses generate methods but remain normal Python classes.\n- Use `field()` for advanced defaults such as per-instance lists or dictionaries.\n:::\n', 'datetime.md': '+++\nslug = "datetime"\ntitle = "Dates and Times"\nsection = "Standard Library"\nsummary = "datetime represents dates, times, durations, formatting, and parsing."\ndoc_path = "/library/datetime.html"\nsee_also = [\n "string-formatting",\n "json",\n "number-parsing",\n]\n+++\n\nThe `datetime` module covers several related ideas: `date` for calendar days, `time` for clock times, `datetime` for both together, and `timedelta` for durations.\n\nTimezone-aware datetimes avoid ambiguity in real systems. `timezone.utc` is a clear default for examples because output stays stable and portable.\n\nUse ISO formatting for interchange, `strftime()` for display, and parsing helpers such as `fromisoformat()` to turn text back into datetime objects.\n\n:::program\n```python\nfrom datetime import date, datetime, time, timedelta, timezone\n\nrelease_day = date(2026, 5, 4)\nmeeting_time = time(12, 30)\ncreated_at = datetime.combine(release_day, meeting_time, tzinfo=timezone.utc)\n\nprint(release_day.isoformat())\nprint(meeting_time.isoformat())\nprint(created_at.isoformat())\n\nexpires_at = created_at + timedelta(days=7, hours=2)\nprint(expires_at.isoformat())\n\nprint(created_at.strftime("%Y-%m-%d %H:%M %Z"))\niso_text = "2026-05-04T12:30:00+00:00"\nparsed = datetime.fromisoformat(iso_text)\nprint(parsed == created_at)\n```\n:::\n\n:::cell\nThe `datetime` module separates calendar dates, clock times, combined datetimes, and durations. Import the types you need explicitly.\n\nUse `date` for a calendar day and `time` for a time of day. Combine them into a timezone-aware `datetime` when you mean an instant.\n\n`isoformat()` produces stable machine-readable text. It is a good default for examples, APIs, and logs.\n\n```python\nfrom datetime import date, datetime, time, timedelta, timezone\n\nrelease_day = date(2026, 5, 4)\nmeeting_time = time(12, 30)\ncreated_at = datetime.combine(release_day, meeting_time, tzinfo=timezone.utc)\n\nprint(release_day.isoformat())\nprint(meeting_time.isoformat())\nprint(created_at.isoformat())\n```\n\n```output\n2026-05-04\n12:30:00\n2026-05-04T12:30:00+00:00\n```\n:::\n\n:::cell\nUse `timedelta` for durations. Adding one to a `datetime` produces another `datetime` without manually changing calendar fields.\n\n```python\nexpires_at = created_at + timedelta(days=7, hours=2)\nprint(expires_at.isoformat())\n```\n\n```output\n2026-05-11T14:30:00+00:00\n```\n:::\n\n:::cell\nUse `strftime()` for human-facing formatting and `fromisoformat()` when reading ISO 8601 text back into a `datetime`.\n\n```python\nprint(created_at.strftime("%Y-%m-%d %H:%M %Z"))\niso_text = "2026-05-04T12:30:00+00:00"\nparsed = datetime.fromisoformat(iso_text)\nprint(parsed == created_at)\n```\n\n```output\n2026-05-04 12:30 UTC\nTrue\n```\n:::\n\n:::note\n- Use timezone-aware datetimes for instants that cross system or user boundaries.\n- Use `date` for calendar days, `time` for clock times, `datetime` for both, and `timedelta` for durations.\n- Prefer ISO 8601 strings for interchange; use `strftime` for human-facing display.\n:::\n', 'decorators.md': '+++\nslug = "decorators"\ntitle = "Decorators"\nsection = "Functions"\nsummary = "Decorators wrap or register functions using @ syntax."\ndoc_path = "/glossary.html#term-decorator"\nsee_also = [\n "closures",\n "functions",\n "callable-types",\n "classmethods-and-staticmethods",\n]\n+++\n\nA decorator is a callable that receives a function and returns a replacement. The `@` syntax applies that transformation at function definition time.\n\nDecorators are common in frameworks because they can register handlers or add behavior while keeping the decorated function focused on the core action.\n\n`@decorator` is shorthand for rebinding a function to the decorator\'s return value. Production wrappers usually use `functools.wraps` so debugging, help text, and framework introspection still see the original function metadata.\n\n:::program\n```python\nfrom functools import wraps\n\n\ndef loud(func):\n @wraps(func)\n def wrapper(name):\n return func(name).upper()\n return wrapper\n\n\ndef greet(name):\n return f"hello {name}"\n\nmanual_greet = loud(greet)\nprint(manual_greet("python"))\n\n@loud\ndef welcome(name):\n """Return a welcome message."""\n return f"welcome {name}"\n\nprint(welcome("workers"))\nprint(welcome.__name__)\nprint(welcome.__doc__)\n```\n:::\n\n:::cell\nA decorator is just a function that takes a function and returns another callable. Applying it manually shows the wrapping step.\n\n```python\nfrom functools import wraps\n\n\ndef loud(func):\n @wraps(func)\n def wrapper(name):\n return func(name).upper()\n return wrapper\n\n\ndef greet(name):\n return f"hello {name}"\n\nmanual_greet = loud(greet)\nprint(manual_greet("python"))\n```\n\n```output\nHELLO PYTHON\n```\n:::\n\n:::cell\nThe `@loud` syntax performs the same rebinding at definition time. After decoration, `welcome` refers to the wrapper returned by `loud`.\n\n```python\n@loud\ndef welcome(name):\n """Return a welcome message."""\n return f"welcome {name}"\n\nprint(welcome("workers"))\n```\n\n```output\nWELCOME WORKERS\n```\n:::\n\n:::cell\n`functools.wraps` copies useful metadata from the original function onto the wrapper.\n\n```python\nprint(welcome.__name__)\nprint(welcome.__doc__)\n```\n\n```output\nwelcome\nReturn a welcome message.\n```\n:::\n\n:::note\n- `@decorator` is shorthand for assigning `func = decorator(func)`.\n- Decorators can wrap, replace, or register functions.\n- Use `functools.wraps` in production wrappers that should preserve metadata.\n:::\n', 'delete-statements.md': '+++\nslug = "delete-statements"\ntitle = "Delete Statements"\nsection = "Data Model"\nsummary = "del removes bindings, items, and attributes rather than producing a value."\ndoc_path = "/reference/simple_stmts.html#the-del-statement"\nsee_also = [\n "variables",\n "dicts",\n "mutability",\n]\n+++\n\n`del` removes a binding or an item. It is a statement, not a function, and it does not return the removed value.\n\nUse `del name` when a name should no longer be bound. Use `del mapping[key]` or `del sequence[index]` when mutating a container by removing one part.\n\nThis is different from assigning `None`: `None` is still a value, while `del` removes the binding or slot.\n\n:::program\n```python\nprofile = {"name": "Ada", "temporary": True}\ndel profile["temporary"]\nprint(profile)\n\nitems = ["a", "b", "c"]\ndel items[1]\nprint(items)\n\nvalue = "cached"\ndel value\nprint("value" in locals())\n```\n:::\n\n:::cell\nDeleting a dictionary key mutates the dictionary. The key is gone; it has not been set to `None`.\n\n```python\nprofile = {"name": "Ada", "temporary": True}\ndel profile["temporary"]\nprint(profile)\n```\n\n```output\n{\'name\': \'Ada\'}\n```\n:::\n\n:::cell\nDeleting a list item removes that position and shifts later items left.\n\n```python\nitems = ["a", "b", "c"]\ndel items[1]\nprint(items)\n```\n\n```output\n[\'a\', \'c\']\n```\n:::\n\n:::cell\nDeleting a name removes the binding from the current namespace. It is different from rebinding the name to `None`.\n\n```python\nvalue = "cached"\ndel value\nprint("value" in locals())\n```\n\n```output\nFalse\n```\n:::\n\n:::note\n- `del` removes bindings or container entries.\n- Assign `None` when absence should remain an explicit value.\n- Use container methods such as `pop()` when you need the removed value back.\n:::\n', 'descriptors.md': '+++\nslug = "descriptors"\ntitle = "Descriptors"\nsection = "Data Model"\nsummary = "Descriptors customize attribute access through __get__, __set__, or __delete__."\ndoc_path = "/howto/descriptor.html"\nsee_also = [\n "attribute-access",\n "properties",\n "bound-and-unbound-methods",\n]\n+++\n\nA descriptor is an object stored on a class that defines `__get__`, `__set__`, or `__delete__`. When an instance attribute lookup finds that object on the class, Python calls the descriptor method instead of returning the descriptor object directly.\n\nDescriptors are the machinery behind methods, `property`, validators, and many ORM fields. Use them when one reusable object should control access for many attributes or classes; use `property` for a single simple managed attribute.\n\nThis example implements a positive-number validator. `__set_name__` learns the attribute name when the owner class is created, `__set__` validates writes, and `__get__` reads the stored value back from the instance.\n\n:::program\n```python\nclass Positive:\n def __set_name__(self, owner, name):\n self.private_name = "_" + name\n\n def __get__(self, obj, owner):\n if obj is None:\n return self\n return getattr(obj, self.private_name)\n\n def __set__(self, obj, value):\n if value <= 0:\n raise ValueError("must be positive")\n setattr(obj, self.private_name, value)\n\nclass Product:\n price = Positive()\n\n def __init__(self, price):\n self.price = price\n\nitem = Product(10)\nprint(item.price)\nprint(Product.price.private_name)\ntry:\n item.price = -1\nexcept ValueError as error:\n print(error)\n```\n:::\n\n:::cell\nA descriptor object lives on the class. `__set_name__` lets it learn which managed attribute it is serving.\n\n```python\nclass Positive:\n def __set_name__(self, owner, name):\n self.private_name = "_" + name\n\n def __get__(self, obj, owner):\n if obj is None:\n return self\n return getattr(obj, self.private_name)\n\n def __set__(self, obj, value):\n if value <= 0:\n raise ValueError("must be positive")\n setattr(obj, self.private_name, value)\n\nclass Product:\n price = Positive()\n\nprint(Product.price.private_name)\n```\n\n```output\n_price\n```\n:::\n\n:::cell\nAssigning `item.price` calls `Positive.__set__`, and reading it calls `Positive.__get__`.\n\n```python\nclass Product:\n price = Positive()\n\n def __init__(self, price):\n self.price = price\n\nitem = Product(10)\nprint(item.price)\ntry:\n item.price = -1\nexcept ValueError as error:\n print(error)\n```\n\n```output\n10\nmust be positive\n```\n:::\n\n:::note\n- Descriptors are class attributes that participate in instance attribute access.\n- Data descriptors with `__set__` can validate or transform assignments.\n- `property` is usually simpler for one-off managed attributes; descriptors shine when the behavior is reusable.\n:::\n', 'dicts.md': '+++\nslug = "dicts"\ntitle = "Dictionaries"\nsection = "Collections"\nsummary = "Dictionaries map keys to values for records, lookup, and structured data."\ndoc_path = "/tutorial/datastructures.html#dictionaries"\nsee_also = [\n "lists",\n "sets",\n "typed-dicts",\n "json",\n]\n+++\n\nDictionaries are Python\'s built-in mapping type. They exist for data where names or keys are more meaningful than numeric positions: records, lookup tables, counters, and JSON-like payloads.\n\nUse direct indexing when a key is required. Use `get()` when absence is expected and the code has a reasonable fallback.\n\nUnlike lists, dictionaries answer “what value belongs to this key?” rather than “what value is at this position?” Iterating with `items()` keeps each key next to its value.\n\n:::program\n```python\nprofile = {"name": "Ada", "language": "Python"}\nprofile["year"] = 1843\nprint(profile["name"])\nprint(profile.get("timezone", "UTC"))\n\nscores = {"Ada": 10, "Grace": 9}\nprint(scores["Grace"])\nprint(scores.get("Guido", 0))\n\nfor name, score in scores.items():\n print(f"{name}: {score}")\n\ninventory = {"apple": 0, "pear": 3, "plum": 0}\nfor name in list(inventory.keys()):\n if inventory[name] == 0:\n del inventory[name]\nprint(inventory)\n```\n:::\n\n:::cell\nUse a dictionary as a small record when fields have names. Direct indexing communicates that the key is required, while `get()` communicates that a missing key has a fallback.\n\n```python\nprofile = {"name": "Ada", "language": "Python"}\nprofile["year"] = 1843\nprint(profile["name"])\nprint(profile.get("timezone", "UTC"))\n```\n\n```output\nAda\nUTC\n```\n:::\n\n:::cell\nUse a dictionary as a lookup table when keys identify values. This is different from a list, where numeric position is the lookup key.\n\n```python\nscores = {"Ada": 10, "Grace": 9}\nprint(scores["Grace"])\nprint(scores.get("Guido", 0))\n```\n\n```output\n9\n0\n```\n:::\n\n:::cell\nUse `items()` when the loop needs both keys and values. It avoids looping over keys and then indexing back into the dictionary.\n\n```python\nfor name, score in scores.items():\n print(f"{name}: {score}")\n```\n\n```output\nAda: 10\nGrace: 9\n```\n:::\n\n:::cell\nAdding or removing keys while iterating a dictionary raises `RuntimeError` ("dictionary changed size during iteration"); reassigning an existing key\'s value is allowed. Snapshot the keys with `list(d.keys())` (or build a list of changes and apply them after the loop) so deletions see a stable view.\n\n```python\ninventory = {"apple": 0, "pear": 3, "plum": 0}\nfor name in list(inventory.keys()):\n if inventory[name] == 0:\n del inventory[name]\nprint(inventory)\n```\n\n```output\n{\'pear\': 3}\n```\n:::\n\n:::note\n- Dictionaries preserve insertion order in modern Python.\n- Use `get()` when a missing key has a reasonable default.\n- Use direct indexing when a missing key should be treated as an error.\n- Snapshot keys with `list(d.keys())` before deleting items in a loop; adding or removing keys during iteration raises `RuntimeError`.\n:::\n', 'enums.md': '+++\nslug = "enums"\ntitle = "Enums"\nsection = "Types"\nsummary = "Enum defines symbolic names for a fixed set of values."\ndoc_path = "/library/enum.html"\nsee_also = [\n "literals",\n "classes",\n "literal-and-final",\n]\n+++\n\n`Enum` defines a fixed set of named values. This makes states and modes easier to read than raw strings scattered through a program.\n\nEach enum member has a name and a value. Comparing enum members is explicit and helps avoid typos that plain strings would allow.\n\nUse enums when a value must be one of a small known set: statuses, modes, directions, roles, and similar choices.\n\n:::program\n```python\nfrom enum import Enum\n\nclass Status(Enum):\n PENDING = "pending"\n DONE = "done"\n\ncurrent = Status.PENDING\nprint(current.name)\nprint(current.value)\nprint(current is Status.PENDING)\nprint(current == "pending")\n```\n:::\n\n:::cell\nAn enum member has a symbolic name and an underlying value. The symbolic name is what readers usually care about in code.\n\n```python\nfrom enum import Enum\n\nclass Status(Enum):\n PENDING = "pending"\n DONE = "done"\n\ncurrent = Status.PENDING\nprint(current.name)\nprint(current.value)\n```\n\n```output\nPENDING\npending\n```\n:::\n\n:::cell\nCompare enum members with enum members, not with raw strings. This keeps the set of valid states explicit.\n\n```python\nprint(current is Status.PENDING)\nprint(current == "pending")\n```\n\n```output\nTrue\nFalse\n```\n:::\n\n:::note\n- Enums make states and choices explicit.\n- Members have names and values.\n- Comparing enum members avoids string typo bugs.\n- Prefer raw strings for open-ended text; prefer enums for a closed set of named choices.\n:::\n', 'equality-and-identity.md': '+++\nslug = "equality-and-identity"\ntitle = "Equality and Identity"\nsection = "Data Model"\nsummary = "== compares values, while is compares object identity."\ndoc_path = "/reference/expressions.html#is-not"\nsee_also = [\n "none",\n "values",\n "object-lifecycle",\n "mutability",\n]\n+++\n\nPython separates equality from identity. Equality asks whether two objects should be considered the same value, while identity asks whether two names point to the same object.\n\nThis distinction matters for mutable containers because two equal lists can still be independent objects. Mutating one should not imply mutating the other unless they share identity.\n\nThe `is` operator is best reserved for identity checks against singletons such as `None`. For ordinary values, `==` is the comparison readers expect.\n\n:::program\n```python\nleft = [1, 2, 3]\nright = [1, 2, 3]\nprint(left == right)\nprint(left is right)\n\nsame = left\nsame.append(4)\nprint(left)\nprint(same is left)\n\nvalue = None\nprint(value is None)\n\nsmall_a = 100\nsmall_b = 100\nprint(small_a is small_b)\n\nbig_a = int("1000")\nbig_b = int("1000")\nprint(big_a is big_b)\nprint(big_a == big_b)\n```\n:::\n\n:::cell\nEqual containers can be different objects. `==` compares list contents, while `is` checks whether both names refer to the same list object.\n\n```python\nleft = [1, 2, 3]\nright = [1, 2, 3]\nprint(left == right)\nprint(left is right)\n```\n\n```output\nTrue\nFalse\n```\n:::\n\n:::cell\nIdentity matters when objects are mutable. `same` is another name for `left`, so mutating through one name changes the object seen through the other.\n\n```python\nsame = left\nsame.append(4)\nprint(left)\nprint(same is left)\n```\n\n```output\n[1, 2, 3, 4]\nTrue\n```\n:::\n\n:::cell\nUse `is` for singleton identity checks such as `None`. This asks whether the value is the one special `None` object.\n\n```python\nvalue = None\nprint(value is None)\n```\n\n```output\nTrue\n```\n:::\n\n:::cell\n`is` for integers is unreliable because CPython caches small integers (roughly `-5` to `256`) but not larger ones. Two equal large integers can be different objects. Use `==` for value comparisons; reserve `is` for singletons.\n\n```python\nsmall_a = 100\nsmall_b = 100\nprint(small_a is small_b)\n\nbig_a = int("1000")\nbig_b = int("1000")\nprint(big_a is big_b)\nprint(big_a == big_b)\n```\n\n```output\nTrue\nFalse\nTrue\n```\n:::\n\n:::note\n- Use `==` for ordinary value comparisons.\n- Use `is` primarily for identity checks against singletons such as `None`.\n- Equal mutable containers can still be independent objects.\n- Never use `is` to compare numbers; CPython\'s small-integer cache makes the result an implementation detail.\n:::\n', 'exception-chaining.md': '+++\nslug = "exception-chaining"\ntitle = "Exception Chaining"\nsection = "Errors"\nsummary = "raise from preserves the original cause when translating exceptions."\ndoc_path = "/tutorial/errors.html#exception-chaining"\nsee_also = [\n "exceptions",\n "custom-exceptions",\n "assertions",\n]\n+++\n\nException chaining connects a higher-level error to the lower-level exception that caused it. The syntax is `raise NewError(...) from error`.\n\nUse chaining when translating implementation details into a domain-specific error while preserving the original cause for debugging.\n\nThis is different from hiding the original exception. The caller can catch the domain error, and tooling can still inspect `__cause__`.\n\n:::program\n```python\nclass ConfigError(Exception):\n pass\n\n\ndef read_port(text):\n try:\n return int(text)\n except ValueError as error:\n raise ConfigError("port must be a number") from error\n\nprint(ConfigError.__name__)\n\ntry:\n read_port("abc")\nexcept ConfigError as error:\n print(error)\n print(type(error.__cause__).__name__)\n```\n:::\n\n:::cell\nCatch the low-level exception where it happens, then raise a domain-specific exception from it.\n\n```python\nclass ConfigError(Exception):\n pass\n\n\ndef read_port(text):\n try:\n return int(text)\n except ValueError as error:\n raise ConfigError("port must be a number") from error\n\nprint(ConfigError.__name__)\n```\n\n```output\nConfigError\n```\n:::\n\n:::cell\nThe caller handles the domain error. The original `ValueError` remains available as `__cause__`.\n\n```python\ntry:\n read_port("abc")\nexcept ConfigError as error:\n print(error)\n print(type(error.__cause__).__name__)\n```\n\n```output\nport must be a number\nValueError\n```\n:::\n\n:::note\n- Use `raise ... from error` when translating exceptions across a boundary.\n- The new exception\'s `__cause__` points to the original exception.\n- Chaining keeps user-facing errors clear without losing debugging context.\n:::\n', 'exception-groups.md': '+++\nslug = "exception-groups"\ntitle = "Exception Groups"\nsection = "Errors"\nsummary = "except* handles matching exceptions inside an ExceptionGroup."\ndoc_path = "/tutorial/errors.html#raising-and-handling-multiple-unrelated-exceptions"\nsee_also = [\n "exceptions",\n "exception-chaining",\n "async-await",\n]\n+++\n\n`ExceptionGroup` represents several unrelated exceptions raised together. `except*` exists for code that may receive multiple failures at once, especially concurrent work.\n\nUse ordinary `except` for one exception. Use `except*` only when the value being handled is an exception group and each matching subgroup needs its own handling.\n\nEach `except*` clause receives a smaller exception group containing the matching exceptions.\n\n:::program\n```python\nerrors = ExceptionGroup(\n "batch failed",\n [ValueError("bad port"), TypeError("bad mode")],\n)\nprint(len(errors.exceptions))\n\ntry:\n raise errors\nexcept* ValueError as group:\n print(type(group).__name__)\n print(group.exceptions[0])\nexcept* TypeError as group:\n print(group.exceptions[0])\n```\n:::\n\n:::cell\nAn exception group bundles several exception objects. This is different from an ordinary exception because more than one failure is present.\n\n```python\nerrors = ExceptionGroup(\n "batch failed",\n [ValueError("bad port"), TypeError("bad mode")],\n)\nprint(len(errors.exceptions))\n```\n\n```output\n2\n```\n:::\n\n:::cell\n`except*` handles matching members of the group. The `ValueError` handler sees the value error, and the `TypeError` handler sees the type error.\n\n```python\ntry:\n raise errors\nexcept* ValueError as group:\n print(type(group).__name__)\n print(group.exceptions[0])\nexcept* TypeError as group:\n print(group.exceptions[0])\n```\n\n```output\nExceptionGroup\nbad port\nbad mode\n```\n:::\n\n:::note\n- `except*` is for `ExceptionGroup`, not ordinary single exceptions.\n- Each `except*` clause handles matching members of the group.\n- Exception groups often appear around concurrent work.\n:::\n', 'exceptions.md': '+++\nslug = "exceptions"\ntitle = "Exceptions"\nsection = "Errors"\nsummary = "Use try, except, else, and finally to separate success, recovery, and cleanup."\ndoc_path = "/tutorial/errors.html"\nsee_also = [\n "conditionals",\n "guard-clauses",\n "custom-exceptions",\n "warnings",\n]\n+++\n\nExceptions represent errors or unusual conditions that interrupt normal control flow. `try` marks the operation that may fail, and `except` handles a specific failure where recovery makes sense.\n\nKeep the successful path separate from the recovery path. `else` runs only when no exception was raised, while `finally` runs either way for cleanup or bookkeeping.\n\nUse exceptions when an operation cannot produce a valid result. Prefer ordinary conditionals for expected branches that are not errors.\n\nCatch specific exceptions whenever possible. A broad catch can hide programming mistakes, while a targeted `ValueError` handler documents exactly what failure is expected.\n\n:::program\n```python\ndef parse_int(text):\n return int(text)\n\nfor text in ["42", "python"]:\n try:\n number = parse_int(text)\n except ValueError:\n print(f"{text}: invalid")\n else:\n print(f"{text}: {number}")\n finally:\n print(f"checked {text}")\n\n\ndef safe_parse_broken(text):\n try:\n return int(text)\n except Exception:\n return None\n\ndef safe_parse_fixed(text):\n try:\n return int(text)\n except ValueError:\n return None\n\nprint(safe_parse_broken("42"))\nprint(safe_parse_fixed("42"))\nprint(safe_parse_broken(["4", "2"]))\n\ntry:\n safe_parse_fixed(["4", "2"])\nexcept TypeError as error:\n print(type(error).__name__)\n```\n:::\n\n:::cell\nWhen no exception is raised, the `else` block runs. Keeping success in `else` makes the `try` block contain only the operation that might fail.\n\n```python\ndef parse_int(text):\n return int(text)\n\ntext = "42"\ntry:\n number = parse_int(text)\nexcept ValueError:\n print(f"{text}: invalid")\nelse:\n print(f"{text}: {number}")\nfinally:\n print(f"checked {text}")\n```\n\n```output\n42: 42\nchecked 42\n```\n:::\n\n:::cell\nWhen parsing fails, `int()` raises `ValueError`. Catching that specific exception makes the expected recovery path explicit.\n\n```python\ntext = "python"\ntry:\n number = parse_int(text)\nexcept ValueError:\n print(f"{text}: invalid")\nelse:\n print(f"{text}: {number}")\nfinally:\n print(f"checked {text}")\n```\n\n```output\npython: invalid\nchecked python\n```\n:::\n\n:::cell\nBare `except:` and broad `except Exception:` swallow far more than the failure you meant to handle, including `KeyboardInterrupt` (bare) and most programming bugs (broad). The two functions look interchangeable on good input — the divergence appears on a buggy call: passing a list is a programming error, yet the broad version converts it into a quiet `None` while the specific version lets the `TypeError` surface.\n\n```python\ndef safe_parse_broken(text):\n try:\n return int(text)\n except Exception:\n return None\n\ndef safe_parse_fixed(text):\n try:\n return int(text)\n except ValueError:\n return None\n\nprint(safe_parse_broken("42"))\nprint(safe_parse_fixed("42"))\nprint(safe_parse_broken(["4", "2"]))\n\ntry:\n safe_parse_fixed(["4", "2"])\nexcept TypeError as error:\n print(type(error).__name__)\n```\n\n```output\n42\n42\nNone\nTypeError\n```\n:::\n\n:::note\n- Catch the most specific exception you can.\n- `else` is for success code that should run only if the `try` block did not fail.\n- `finally` runs whether the operation succeeded or failed.\n- Avoid bare `except:` and broad `except Exception:` — they hide bugs and absorb signals like `KeyboardInterrupt`.\n:::\n', 'for-loops.md': '+++\nslug = "for-loops"\ntitle = "For Loops"\nsection = "Control Flow"\nsummary = "for iterates over values produced by an iterable."\ndoc_path = "/tutorial/controlflow.html#for-statements"\nsee_also = [\n "while-loops",\n "iterating-over-iterables",\n "iterators",\n]\n+++\n\nA `for` loop asks an iterable for values and runs the indented block once per value. Python\'s loop is not primarily a numeric counter; it is a consumer of lists, ranges, files, generators, and any object that implements the iterator protocol.\n\nPrefer direct iteration when you need each value. Use `range()` when the numbers themselves are the data, and use `enumerate()` when the position and the value both matter.\n\nThe loop body is the indented block. When the iterable is exhausted, execution continues after the block. The neighboring `while` loop shape is for conditions that must be rechecked manually.\n\n:::program\n```python\nfor name in ["Ada", "Grace", "Guido"]:\n print(name)\n\nfor number in range(3):\n print(number)\n\nfor index, name in enumerate(["Ada", "Grace"], start=1):\n print(index, name)\n```\n:::\n\n:::cell\nDirect iteration keeps the code focused on the values in the collection.\n\n```python\nfor name in ["Ada", "Grace", "Guido"]:\n print(name)\n```\n\n```output\nAda\nGrace\nGuido\n```\n:::\n\n:::cell\n`range(3)` yields `0`, `1`, and `2` lazily. Use it when those integers are the thing being iterated over.\n\n```python\nfor number in range(3):\n print(number)\n```\n\n```output\n0\n1\n2\n```\n:::\n\n:::cell\n`enumerate()` is the usual Python way to keep a counter beside each value without indexing back into the list.\n\n```python\nfor index, name in enumerate(["Ada", "Grace"], start=1):\n print(index, name)\n```\n\n```output\n1 Ada\n2 Grace\n```\n:::\n\n:::note\n- A `for` loop consumes an iterable until it is exhausted.\n- Reach for `while` when the stopping condition must be rechecked manually.\n- `iter()` and `next()` expose the protocol that `for` uses internally.\n:::\n', 'functions.md': '+++\nslug = "functions"\ntitle = "Functions"\nsection = "Functions"\nsummary = "Use def to name reusable behavior and return results."\ndoc_path = "/tutorial/controlflow.html#defining-functions"\nsee_also = [\n "variables",\n "args-and-kwargs",\n "keyword-only-arguments",\n "closures",\n]\n+++\n\nFunctions package behavior behind a name. `def` creates a function object that can accept arguments, compute values, and return a result.\n\nDefault arguments make common calls short, and keyword arguments make call sites easier to read. A function that reaches the end without `return` produces `None`.\n\nUse functions when a calculation has a useful name, when code repeats, or when a piece of behavior should be tested independently.\n\n:::program\n```python\ndef greet(name):\n return f"Hello, {name}."\n\nprint(greet("Python"))\n\n\ndef format_total(amount, currency="USD"):\n return f"{amount} {currency}"\n\nprint(format_total(10))\nprint(format_total(10, currency="EUR"))\n\n\ndef log(message):\n print(f"log: {message}")\n\nresult = log("saved")\nprint(result)\n\n\ndef append_broken(item, items=[]):\n items.append(item)\n return items\n\nprint(append_broken("a"))\nprint(append_broken("b"))\n\n\ndef append_fixed(item, items=None):\n if items is None:\n items = []\n items.append(item)\n return items\n\nprint(append_fixed("a"))\nprint(append_fixed("b"))\n```\n:::\n\n:::cell\n`return` sends a value back to the caller. The caller can print it, store it, or pass it to another function.\n\n```python\ndef greet(name):\n return f"Hello, {name}."\n\nprint(greet("Python"))\n```\n\n```output\nHello, Python.\n```\n:::\n\n:::cell\nDefault arguments provide common values. Keyword arguments make it clear which option is being overridden.\n\n```python\ndef format_total(amount, currency="USD"):\n return f"{amount} {currency}"\n\nprint(format_total(10))\nprint(format_total(10, currency="EUR"))\n```\n\n```output\n10 USD\n10 EUR\n```\n:::\n\n:::cell\nA function without an explicit `return` returns `None`. That makes side-effect-only functions easy to distinguish from value-producing ones.\n\n```python\ndef log(message):\n print(f"log: {message}")\n\nresult = log("saved")\nprint(result)\n```\n\n```output\nlog: saved\nNone\n```\n:::\n\n:::cell\nMutable default arguments are evaluated once when the function is defined, not on each call. The same list is shared across calls, so successive calls see each other\'s mutations. Use `None` as the sentinel and create a fresh container inside the body.\n\n```python\ndef append_broken(item, items=[]):\n items.append(item)\n return items\n\nprint(append_broken("a"))\nprint(append_broken("b"))\n\n\ndef append_fixed(item, items=None):\n if items is None:\n items = []\n items.append(item)\n return items\n\nprint(append_fixed("a"))\nprint(append_fixed("b"))\n```\n\n```output\n[\'a\']\n[\'a\', \'b\']\n[\'a\']\n[\'b\']\n```\n:::\n\n:::note\n- Use `return` for values the caller should receive.\n- Defaults keep common calls concise.\n- Keyword arguments make options readable at the call site.\n- Never use a mutable value as a default argument; use `None` and build the container inside the function body.\n:::\n', 'generator-expressions.md': '+++\nslug = "generator-expressions"\ntitle = "Generator Expressions"\nsection = "Iteration"\nsummary = "Generator expressions use comprehension-like syntax to stream values lazily."\ndoc_path = "/tutorial/classes.html#generator-expressions"\nsee_also = [\n "comprehensions",\n "generators",\n "itertools",\n "yield-from",\n]\n+++\n\nGenerator expressions look like list comprehensions with parentheses, but they produce an iterator instead of building a concrete collection immediately.\n\nUse them when a consumer such as `sum()`, `any()`, or a `for` loop can use values one at a time. This keeps the transformation close to the consumer and avoids storing intermediate lists.\n\nLike other iterators, a generator expression is consumed as values are requested. Create a new generator expression when you need another pass.\n\n:::program\n```python\nnumbers = [1, 2, 3, 4]\nlist_squares = [number * number for number in numbers]\nprint(list_squares)\n\nstream_squares = (number * number for number in numbers)\nprint(next(stream_squares))\nprint(next(stream_squares))\nprint(list(stream_squares))\n\nprint(sum(number * number for number in numbers))\n```\n:::\n\n:::cell\nA list comprehension is eager: it builds a list immediately. That is useful when you need to store or reuse the results.\n\n```python\nnumbers = [1, 2, 3, 4]\nlist_squares = [number * number for number in numbers]\nprint(list_squares)\n```\n\n```output\n[1, 4, 9, 16]\n```\n:::\n\n:::cell\nA generator expression is lazy: it creates an iterator that produces values as they are consumed. After two `next()` calls, only the remaining squares are left.\n\n```python\nstream_squares = (number * number for number in numbers)\nprint(next(stream_squares))\nprint(next(stream_squares))\nprint(list(stream_squares))\n```\n\n```output\n1\n4\n[9, 16]\n```\n:::\n\n:::cell\nGenerator expressions are common inside reducing functions. When a generator expression is the only argument, the extra parentheses can be omitted.\n\n```python\nprint(sum(number * number for number in numbers))\n```\n\n```output\n30\n```\n:::\n\n:::note\n- List, dict, and set comprehensions build concrete collections.\n- Generator expressions produce one-pass iterators.\n- Use generator expressions when the consumer can process values one at a time.\n:::\n', 'generators.md': '+++\nslug = "generators"\ntitle = "Generators"\nsection = "Iteration"\nsummary = "yield creates an iterator that produces values on demand."\ndoc_path = "/tutorial/classes.html#generators"\nsee_also = [\n "iterators",\n "iterator-vs-iterable",\n "generator-expressions",\n]\n+++\n\nA generator function is a convenient way to write your own iterator. `yield` produces one value, pauses the function, and resumes when the next value is requested.\n\nGenerators are useful for pipelines, large inputs, and infinite sequences because they avoid building an entire collection in memory.\n\nUse `next()` to request one value manually, or loop over the generator to consume values until it is exhausted.\n\n:::program\n```python\ndef countdown(n):\n while n > 0:\n yield n\n n -= 1\n\nnumbers = countdown(3)\nprint(next(numbers))\nprint(next(numbers))\n\nfor value in countdown(3):\n print(value)\n\ndef countdown_eager(n):\n result = []\n while n > 0:\n result.append(n)\n n -= 1\n return result\n\nvalues = countdown_eager(3)\nprint(values)\nprint(values)\n\nstream = countdown(3)\nprint(list(stream))\nprint(list(stream))\n\nclass Countdown:\n def __init__(self, n):\n self.n = n\n\n def __iter__(self):\n return self\n\n def __next__(self):\n if self.n <= 0:\n raise StopIteration\n value = self.n\n self.n -= 1\n return value\n\nprint(list(Countdown(3)))\n```\n:::\n\n:::cell\nCalling a generator function returns an iterator. `next()` asks for one value and resumes the function until the next `yield`.\n\n```python\ndef countdown(n):\n while n > 0:\n yield n\n n -= 1\n\nnumbers = countdown(3)\nprint(next(numbers))\nprint(next(numbers))\n```\n\n```output\n3\n2\n```\n:::\n\n:::cell\nA `for` loop repeatedly calls `next()` for you. The loop stops when the generator is exhausted.\n\n```python\nfor value in countdown(3):\n print(value)\n```\n\n```output\n3\n2\n1\n```\n:::\n\n:::cell\n`return` builds the entire result before handing it back; `yield` produces values on demand. The list keeps its values for repeated use, while the generator is exhausted after one pass.\n\n```python\ndef countdown_eager(n):\n result = []\n while n > 0:\n result.append(n)\n n -= 1\n return result\n\nvalues = countdown_eager(3)\nprint(values)\nprint(values)\n\nstream = countdown(3)\nprint(list(stream))\nprint(list(stream))\n```\n\n```output\n[3, 2, 1]\n[3, 2, 1]\n[3, 2, 1]\n[]\n```\n:::\n\n:::cell\nEvery generator is an iterator. The same countdown written by hand needs `__iter__` and `__next__` and an explicit `StopIteration`. The generator function expresses the same protocol with one `yield`.\n\n```python\nclass Countdown:\n def __init__(self, n):\n self.n = n\n\n def __iter__(self):\n return self\n\n def __next__(self):\n if self.n <= 0:\n raise StopIteration\n value = self.n\n self.n -= 1\n return value\n\nprint(list(Countdown(3)))\n```\n\n```output\n[3, 2, 1]\n```\n:::\n\n:::note\n- Generator functions are a concise way to create custom iterators; every generator is an iterator.\n- `yield` defers work and streams values; `return` produces the whole result up front.\n- A generator is consumed as you iterate over it.\n- Prefer a list when you need to reuse stored results; prefer a generator when values can be streamed once.\n:::\n', 'generics-and-typevar.md': '+++\nslug = "generics-and-typevar"\ntitle = "Generics and TypeVar"\nsection = "Types"\nsummary = "Generics preserve type information across reusable functions and classes."\ndoc_path = "/library/typing.html#generics"\nsee_also = [\n "type-hints",\n "collections-module",\n "casts-and-any",\n]\n+++\n\nGenerics connect types across an API. A plain function that returns `object` loses information; a generic function can say that the returned value has the same type as the input element.\n\nA `TypeVar` stands for a type chosen by the caller. In `list[T] -> T`, the same `T` says that a list of strings produces a string and a list of integers produces an integer.\n\nUse generics when a function or class is reusable but still preserves a relationship between input and output types.\n\n:::program\n```python\nfrom typing import TypeVar\n\nT = TypeVar("T")\n\n\ndef first(items: list[T]) -> T:\n return items[0]\n\n\ndef pair(left: T, right: T) -> tuple[T, T]:\n return (left, right)\n\nprint(first([1, 2, 3]))\nprint(first(["Ada", "Grace"]))\nprint(pair("x", "y"))\nprint(T.__name__)\n```\n:::\n\n:::cell\nA `TypeVar` stands for a type chosen by the caller. The return type follows the list element type.\n\n```python\nfrom typing import TypeVar\n\nT = TypeVar("T")\n\n\ndef first(items: list[T]) -> T:\n return items[0]\n\nprint(first([1, 2, 3]))\nprint(first(["Ada", "Grace"]))\n```\n\n```output\n1\nAda\n```\n:::\n\n:::cell\nReusing the same `TypeVar` expresses a relationship between parameters and results.\n\n```python\ndef pair(left: T, right: T) -> tuple[T, T]:\n return (left, right)\n\nprint(pair("x", "y"))\n```\n\n```output\n(\'x\', \'y\')\n```\n:::\n\n:::cell\n`TypeVar` is visible at runtime, but the relationship is mainly for type checkers.\n\n```python\nprint(T.__name__)\nprint(first.__annotations__)\n```\n\n```output\nT\n{\'items\': list[~T], \'return\': ~T}\n```\n:::\n\n:::note\n- A `TypeVar` stands for a type chosen by the caller.\n- Python 3.12+ also accepts the inline PEP 695 spelling `def first[T](items: list[T]) -> T`, which declares the variable without a separate `TypeVar` line; the explicit form shown here works everywhere and reads the same way.\n- Generic functions avoid losing information to `object` or `Any`.\n- Use generics when input and output types are connected.\n:::\n', 'guard-clauses.md': '+++\nslug = "guard-clauses"\ntitle = "Guard Clauses"\nsection = "Control Flow"\nsummary = "Guard clauses handle boundary cases early so the main path stays flat."\ndoc_path = "/tutorial/controlflow.html#if-statements"\nsee_also = [\n "conditionals",\n "exceptions",\n "functions",\n]\n+++\n\nA guard clause is an early `return`, `raise`, `break`, or `continue` that handles a case the rest of the function should not process. The point is not new syntax; the point is moving boundaries out of the way so the successful path can be read straight through.\n\nUse guards when a function has clear invalid, empty, or already-finished cases. If every branch is equally important, an ordinary `if`/`elif` chain may be clearer.\n\nThe contrast below shows the payoff: the nested version makes the valid path live inside two conditions, while the guard version names the invalid cases first and leaves the calculation at the outer indentation level.\n\n:::program\n```python\ndef nested_discount(price, percent):\n if price >= 0:\n if 0 <= percent <= 100:\n return round(price - price * percent / 100, 2)\n return "invalid discount"\n return "invalid price"\n\n\ndef guarded_discount(price, percent):\n if price < 0:\n return "invalid price"\n if not 0 <= percent <= 100:\n return "invalid discount"\n\n return round(price - price * percent / 100, 2)\n\nprint(nested_discount(100, 15))\nprint(guarded_discount(-5, 10))\nprint(guarded_discount(100, 120))\n```\n:::\n\n:::cell\nThe nested version is correct, but the useful work is buried inside both tests.\n\n```python\ndef nested_discount(price, percent):\n if price >= 0:\n if 0 <= percent <= 100:\n return round(price - price * percent / 100, 2)\n return "invalid discount"\n return "invalid price"\n\nprint(nested_discount(100, 15))\n```\n\n```output\n85.0\n```\n:::\n\n:::cell\nThe guard-clause version handles impossible inputs first, then lets the ordinary calculation sit at the top level of the function body.\n\n```python\ndef guarded_discount(price, percent):\n if price < 0:\n return "invalid price"\n if not 0 <= percent <= 100:\n return "invalid discount"\n\n return round(price - price * percent / 100, 2)\n\nprint(guarded_discount(-5, 10))\nprint(guarded_discount(100, 120))\n```\n\n```output\ninvalid price\ninvalid discount\n```\n:::\n\n:::note\n- Guard clauses are a readability pattern, not a separate Python feature.\n- They work best when the early cases are true boundaries.\n- For exceptional failures, raise an exception instead of returning a sentinel string.\n:::\n', 'hello-world.md': '+++\nslug = "hello-world"\ntitle = "Hello World"\nsection = "Basics"\nsummary = "The first Python program prints a line of text."\ndoc_path = "/tutorial/introduction.html"\nsee_also = [\n "values",\n "variables",\n]\n+++\n\nEvery Python program starts by executing statements from top to bottom. Calling `print()` is the smallest useful program because it shows how Python evaluates an expression and sends text to standard output.\n\nStrings are ordinary values, so the message passed to `print()` can be changed, stored in a variable, or produced by a function. This example keeps the first program intentionally small.\n\nRun the program and compare what you see with the source: the text appears on standard output followed by a newline, which is why successive `print()` calls land on separate lines.\n\n:::program\n```python\nprint("hello world")\n```\n:::\n\n:::cell\nThe whole program is one statement. Python evaluates the string literal `"hello world"` and passes that value to `print()`, which writes it to standard output — the output panel shows exactly that line.\n\n```python\nprint("hello world")\n```\n\n```output\nhello world\n```\n:::\n\n:::note\n- `print()` writes text followed by a newline.\n- Strings can be delimited with single or double quotes.\n:::\n', 'import-aliases.md': '+++\nslug = "import-aliases"\ntitle = "Import Aliases"\nsection = "Modules"\nsummary = "as gives imported modules or names a local alias."\ndoc_path = "/reference/simple_stmts.html#the-import-statement"\nsee_also = [\n "modules",\n "functions",\n]\n+++\n\n`as` gives an imported module or imported name a local alias. Use it when a conventional short name improves readability or when two imports would otherwise collide.\n\nThe alternative is a plain import, which is usually better when the module name is already clear. Avoid aliases that make readers guess where a name came from.\n\nAvoid star imports in examples and production modules because they hide dependencies and blur the boundary between modules.\n\n:::program\n```python\nimport statistics as stats\nfrom math import sqrt as square_root\n\nscores = [8, 10, 9]\nprint(stats.mean(scores))\nprint(stats.__name__)\n\nprint(square_root(81))\nprint(square_root.__name__)\n```\n:::\n\n:::cell\nA module alias keeps the namespace but changes the local name. Here `stats` is shorter, but readers can still see that `mean` belongs to the statistics module.\n\n```python\nimport statistics as stats\n\nscores = [8, 10, 9]\nprint(stats.mean(scores))\nprint(stats.__name__)\n```\n\n```output\n9\nstatistics\n```\n:::\n\n:::cell\nA name imported with `from` can also be aliased. Use this when the local name explains the role better than the original name.\n\n```python\nfrom math import sqrt as square_root\n\nprint(square_root(81))\nprint(square_root.__name__)\n```\n\n```output\n9.0\nsqrt\n```\n:::\n\n:::note\n- `import module as alias` keeps module-style access under a shorter or clearer name.\n- `from module import name as alias` imports one name under a local alias.\n- Prefer plain imports unless an alias improves clarity or follows a strong convention.\n- Avoid `from module import *` because it makes dependencies harder to see.\n:::\n', 'inheritance-and-super.md': '+++\nslug = "inheritance-and-super"\ntitle = "Inheritance and Super"\nsection = "Classes"\nsummary = "Inheritance reuses behavior, and super delegates to a parent implementation."\ndoc_path = "/tutorial/classes.html#inheritance"\nsee_also = [\n "classes",\n "abstract-base-classes",\n "classmethods-and-staticmethods",\n "special-methods",\n]\n+++\n\nInheritance lets one class specialize another class. The child class gets parent behavior and can add or override methods.\n\nUse `super()` when the child method should extend the parent implementation instead of replacing it entirely.\n\nPrefer composition when objects merely collaborate. Inheritance is best when the child really is a specialized version of the parent.\n\n:::program\n```python\nclass Animal:\n def __init__(self, name):\n self.name = name\n\n def speak(self):\n return f"{self.name} makes a sound"\n\nclass Dog(Animal):\n def speak(self):\n base = super().speak()\n return f"{base}; {self.name} barks"\n\npet = Dog("Nina")\nprint(pet.name)\nprint(pet.speak())\nprint(isinstance(pet, Animal))\n```\n:::\n\n:::cell\nA child class names its parent in parentheses. `Dog` instances get the `Animal.__init__` method because `Dog` does not define its own initializer.\n\n```python\nclass Animal:\n def __init__(self, name):\n self.name = name\n\n def speak(self):\n return f"{self.name} makes a sound"\n\nclass Dog(Animal):\n def speak(self):\n base = super().speak()\n return f"{base}; {self.name} barks"\n\npet = Dog("Nina")\nprint(pet.name)\n```\n\n```output\nNina\n```\n:::\n\n:::cell\n`super()` delegates to the parent implementation. The child method can reuse the parent result and then add specialized behavior.\n\n```python\nprint(pet.speak())\nprint(isinstance(pet, Animal))\n```\n\n```output\nNina makes a sound; Nina barks\nTrue\n```\n:::\n\n:::note\n- Inheritance models an “is a specialized kind of” relationship.\n- `super()` calls the next implementation in the method resolution order.\n- Prefer composition when an object only needs to use another object.\n:::\n', 'iterating-over-iterables.md': '+++\nslug = "iterating-over-iterables"\ntitle = "Iterating over Iterables"\nsection = "Iteration"\nsummary = "for loops consume values from any iterable object."\ndoc_path = "/tutorial/controlflow.html#for-statements"\nsee_also = [\n "iterators",\n "iterator-vs-iterable",\n "for-loops",\n]\n+++\n\nPython\'s `for` statement consumes values from any iterable object: lists, strings, dictionaries, ranges, generators, files, and many standard-library helpers.\n\nThis makes iteration a value-stream protocol rather than a special case for arrays. The producer decides how values are made, and the loop consumes them one at a time.\n\nUse `enumerate()` when you need positions and values together, and `dict.items()` when you need keys and values. These helpers express intent better than manual indexing.\n\n:::program\n```python\nnames = ["Ada", "Grace", "Guido"]\n\nfor name in names:\n print(name)\n\nfor index, name in enumerate(names):\n print(index, name)\n\nscores = {"Ada": 10, "Grace": 9}\nfor name, score in scores.items():\n print(name, score)\n```\n:::\n\n:::cell\nStart with an ordinary list. A list stores values, and a `for` loop asks it for one value at a time.\n\nWhen you only need the values, iterate over the collection directly. There is no index variable because the loop body does not need one.\n\n```python\nnames = ["Ada", "Grace", "Guido"]\n\nfor name in names:\n print(name)\n```\n\n```output\nAda\nGrace\nGuido\n```\n:::\n\n:::cell\nWhen you need both a position and a value, use `enumerate()`. It produces index/value pairs without manual indexing.\n\n```python\nfor index, name in enumerate(names):\n print(index, name)\n```\n\n```output\n0 Ada\n1 Grace\n2 Guido\n```\n:::\n\n:::cell\nDictionaries are iterable too, but `dict.items()` is the clearest way to say that the loop needs keys and values together.\n\n```python\nscores = {"Ada": 10, "Grace": 9}\nfor name, score in scores.items():\n print(name, score)\n```\n\n```output\nAda 10\nGrace 9\n```\n:::\n\n:::note\n- A `for` loop consumes values from an iterable.\n- Different producers can feed the same loop protocol.\n- Prefer `enumerate()` over `range(len(...))` when you need an index.\n:::\n', 'iterator-vs-iterable.md': '+++\nslug = "iterator-vs-iterable"\ntitle = "Iterator vs Iterable"\nsection = "Iteration"\nsummary = "Iterables produce fresh iterators; iterators are one-pass."\ndoc_path = "/glossary.html#term-iterable"\nsee_also = [\n "iterators",\n "iterating-over-iterables",\n "generators",\n]\n+++\n\nAn iterable can produce values when asked. An iterator is the object that remembers where the production currently is. The distinction matters because iterables can be traversed many times, while many iterators can be traversed only once.\n\n`iter(iterable)` returns a fresh iterator each call. `iter(iterator)` returns the iterator itself. That self-iteration property is how `for` loops can accept either kind, and it is also why a function that loops over its argument twice silently breaks when called with a generator instead of a list.\n\nThe takeaway for API design: receive iterables when the caller may want a second pass, and materialize once at the boundary if you must.\n\n:::program\n```python\nnames = ["Ada", "Grace"]\n\nprint(list(names))\nprint(list(names))\n\nstream = iter(names)\nprint(list(stream))\nprint(list(stream))\n\nfirst = iter(names)\nsecond = iter(names)\nprint(first is second)\nprint(iter(first) is first)\n\ndef total_and_count(numbers):\n total = sum(numbers)\n count = sum(1 for _ in numbers)\n return total, count\n\ndef values():\n yield from [10, 9, 8]\n\nprint(total_and_count([10, 9, 8]))\nprint(total_and_count(values()))\n\ndef total_and_count_safe(numbers):\n items = list(numbers)\n return sum(items), len(items)\n\nprint(total_and_count_safe(values()))\n```\n:::\n\n:::cell\nA list is iterable. Each `for` loop or `list()` call asks the list for a fresh iterator under the hood, so the same data can be traversed many times.\n\n```python\nnames = ["Ada", "Grace"]\nprint(list(names))\nprint(list(names))\n```\n\n```output\n[\'Ada\', \'Grace\']\n[\'Ada\', \'Grace\']\n```\n:::\n\n:::cell\nAn iterator is one-pass. Calling `iter()` returns a position-tracking object; once it has been exhausted, it stays exhausted.\n\n```python\nstream = iter(names)\nprint(list(stream))\nprint(list(stream))\n```\n\n```output\n[\'Ada\', \'Grace\']\n[]\n```\n:::\n\n:::cell\nCalling `iter()` on an iterable returns a brand-new iterator each time. Calling `iter()` on an iterator returns the same object — that is the rule that lets a `for` loop accept either kind.\n\n```python\nfirst = iter(names)\nsecond = iter(names)\nprint(first is second)\nprint(iter(first) is first)\n```\n\n```output\nFalse\nTrue\n```\n:::\n\n:::cell\nThe distinction shows up at API boundaries. A function that loops over its argument twice works for an iterable but silently produces wrong answers for an iterator, because the second pass finds the iterator already exhausted. Materialize once at the boundary when both passes matter.\n\n```python\ndef total_and_count(numbers):\n total = sum(numbers)\n count = sum(1 for _ in numbers)\n return total, count\n\ndef values():\n yield from [10, 9, 8]\n\nprint(total_and_count([10, 9, 8]))\nprint(total_and_count(values()))\n\ndef total_and_count_safe(numbers):\n items = list(numbers)\n return sum(items), len(items)\n\nprint(total_and_count_safe(values()))\n```\n\n```output\n(27, 3)\n(27, 0)\n(27, 3)\n```\n:::\n\n:::note\n- An iterable produces an iterator each time `iter()` is called on it; an iterator produces values until it is exhausted.\n- `iter(iterable)` returns a fresh iterator; `iter(iterator)` returns the same iterator.\n- Functions that traverse their input more than once must accept an iterable or materialize the input at the boundary.\n:::\n', 'iterators.md': '+++\nslug = "iterators"\ntitle = "Iterators"\nsection = "Iteration"\nsummary = "iter and next expose the protocol behind for loops."\ndoc_path = "/library/stdtypes.html#iterator-types"\nsee_also = [\n "iterating-over-iterables",\n "iterator-vs-iterable",\n "generators",\n]\n+++\n\nAn iterable is an object that can produce values for a loop. An iterator is the object that remembers where that production currently is.\n\n`iter()` asks an iterable for an iterator, and `next()` consumes one value from that iterator. A `for` loop performs those steps for you until the iterator is exhausted.\n\nThis is the core value-stream protocol in Python: one object produces values, another piece of code consumes them, and many streams are one-pass.\n\n:::program\n```python\nnames = ["Ada", "Grace", "Guido"]\niterator = iter(names)\nprint(next(iterator))\nprint(next(iterator))\n\nfor name in iterator:\n print(name)\n\nagain = iter(names)\nprint(next(again))\n```\n:::\n\n:::cell\n`iter()` asks an iterable for an iterator. `next()` consumes one value and advances the iterator\'s position.\n\n```python\nnames = ["Ada", "Grace", "Guido"]\niterator = iter(names)\nprint(next(iterator))\nprint(next(iterator))\n```\n\n```output\nAda\nGrace\n```\n:::\n\n:::cell\nA `for` loop consumes the same iterator protocol. Because two values were already consumed, the loop sees only the remaining value.\n\n```python\nfor name in iterator:\n print(name)\n```\n\n```output\nGuido\n```\n:::\n\n:::cell\nThe list itself is reusable. Asking it for a fresh iterator starts a new pass over the same stored values.\n\n```python\nagain = iter(names)\nprint(next(again))\n```\n\n```output\nAda\n```\n:::\n\n:::note\n- Iterables produce iterators; iterators produce values.\n- `next()` consumes one value from an iterator.\n- Many iterators are one-pass even when the original collection is reusable.\n:::\n', 'itertools.md': '+++\nslug = "itertools"\ntitle = "Itertools"\nsection = "Iteration"\nsummary = "itertools composes lazy iterator streams."\ndoc_path = "/library/itertools.html"\nsee_also = [\n "iterators",\n "generator-expressions",\n "sentinel-iteration",\n "comprehension-patterns",\n]\n+++\n\nThe `itertools` module contains tools for composing iterator streams: combining, slicing, grouping, and repeating values without changing the consumer protocol.\n\nMany `itertools` functions are lazy. They describe work to do later instead of building a list immediately, so helpers such as `islice()` are useful when taking a finite window.\n\nIterator pipelines let each step stay small: one object produces values, another transforms them, and a final consumer such as `list()` or a loop pulls values through the pipeline.\n\n:::program\n```python\nimport itertools\n\ncounter = itertools.count(10)\nprint(list(itertools.islice(counter, 3)))\n\npages = itertools.chain(["intro", "setup"], ["deploy"])\nprint(list(pages))\n\nscores = [7, 10, 8, 10]\nhigh_scores = itertools.compress(scores, [score >= 9 for score in scores])\nprint(list(high_scores))\n```\n:::\n\n:::cell\n`count()` can produce values forever, so `islice()` takes a finite window. Nothing is materialized until `list()` consumes the iterator.\n\n```python\nimport itertools\n\ncounter = itertools.count(10)\nprint(list(itertools.islice(counter, 3)))\n```\n\n```output\n[10, 11, 12]\n```\n:::\n\n:::cell\n`chain()` presents several iterables as one stream. This avoids building an intermediate list just to loop over combined inputs.\n\n```python\npages = itertools.chain(["intro", "setup"], ["deploy"])\nprint(list(pages))\n```\n\n```output\n[\'intro\', \'setup\', \'deploy\']\n```\n:::\n\n:::cell\nIterator helpers compose with ordinary Python expressions. `compress()` keeps items whose corresponding selector is true.\n\n```python\nscores = [7, 10, 8, 10]\nhigh_scores = itertools.compress(scores, [score >= 9 for score in scores])\nprint(list(high_scores))\n```\n\n```output\n[10, 10]\n```\n:::\n\n:::note\n- `itertools` composes producer and transformer streams.\n- Iterator pipelines avoid building intermediate lists.\n- Use `islice()` to take a finite piece from an infinite iterator.\n- Convert to a list only when you need concrete results.\n:::\n', 'json.md': '+++\nslug = "json"\ntitle = "JSON"\nsection = "Standard Library"\nsummary = "json encodes Python values as JSON text and decodes them back."\ndoc_path = "/library/json.html"\nsee_also = [\n "dicts",\n "typed-dicts",\n "strings",\n]\n+++\n\nThe `json` module converts between Python values and JSON text. Dictionaries, lists, strings, numbers, booleans, and `None` map naturally to JSON structures.\n\nUse `dumps()` when you need a string and `loads()` when you need Python objects back. Options such as `sort_keys=True` and `indent=2` control stable, readable output.\n\nJSON is a data format, not a way to preserve arbitrary Python objects. Encode simple data structures at service boundaries, and expect decode errors when the incoming text is not valid JSON.\n\n:::program\n```python\nimport json\n\npayload = {"language": "Python", "versions": [3, 13], "stable": True, "missing": None}\ntext = json.dumps(payload, sort_keys=True)\nprint(text)\n\npretty = json.dumps({"language": "Python", "stable": True}, indent=2, sort_keys=True)\nprint(pretty.splitlines()[0])\nprint(pretty.splitlines()[1])\n\ndecoded = json.loads(text)\nprint(decoded["language"])\nprint(decoded["missing"] is None)\n\ntry:\n json.loads("{bad json}")\nexcept json.JSONDecodeError as error:\n print(error.__class__.__name__)\n```\n:::\n\n:::cell\n`dumps()` encodes Python data as JSON text. `sort_keys=True` keeps dictionary keys in a stable order for reproducible output.\n\n```python\nimport json\n\npayload = {"language": "Python", "versions": [3, 13], "stable": True, "missing": None}\ntext = json.dumps(payload, sort_keys=True)\nprint(text)\n```\n\n```output\n{"language": "Python", "missing": null, "stable": true, "versions": [3, 13]}\n```\n:::\n\n:::cell\nFormatting options change the JSON text, not the Python value. `indent=2` is useful for human-readable output.\n\n```python\npretty = json.dumps({"language": "Python", "stable": True}, indent=2, sort_keys=True)\nprint(pretty.splitlines()[0])\nprint(pretty.splitlines()[1])\n```\n\n```output\n{\n "language": "Python",\n```\n:::\n\n:::cell\n`loads()` decodes JSON text back into Python values. JSON `null` becomes Python `None`.\n\n```python\ndecoded = json.loads(text)\nprint(decoded["language"])\nprint(decoded["missing"] is None)\n```\n\n```output\nPython\nTrue\n```\n:::\n\n:::cell\nInvalid JSON raises `JSONDecodeError`, so input boundaries should handle decode failures explicitly.\n\n```python\ntry:\n json.loads("{bad json}")\nexcept json.JSONDecodeError as error:\n print(error.__class__.__name__)\n```\n\n```output\nJSONDecodeError\n```\n:::\n\n:::note\n- `dumps()` returns a string; `loads()` accepts a string.\n- JSON `true`, `false`, and `null` become Python `True`, `False`, and `None`.\n- Use `sort_keys=True` when stable text output matters.\n- JSON only represents data shapes, not arbitrary Python objects or behavior.\n:::\n', 'keyword-only-arguments.md': '+++\nslug = "keyword-only-arguments"\ntitle = "Keyword-only Arguments"\nsection = "Functions"\nsummary = "Use * to require selected function arguments to be named."\ndoc_path = "/tutorial/controlflow.html#special-parameters"\nsee_also = [\n "functions",\n "args-and-kwargs",\n "positional-only-parameters",\n "partial-functions",\n]\n+++\n\nA bare `*` in a function signature marks the following parameters as keyword-only. Callers must name those arguments explicitly.\n\nKeyword-only arguments are useful for options such as timeouts, flags, and modes where positional calls would be ambiguous or easy to misread.\n\nThey let the required data stay positional while optional controls remain self-documenting at the call site.\n\n:::program\n```python\ndef connect(host, *, timeout=5, secure=True):\n scheme = "https" if secure else "http"\n print(f"{scheme}://{host} timeout={timeout}")\n\nconnect("example.com")\nconnect("example.com", timeout=10)\nconnect("localhost", secure=False)\n\ntry:\n connect("example.com", 10)\nexcept TypeError as error:\n print(type(error).__name__)\n```\n:::\n\n:::cell\nParameters after `*` must be named. The default options still apply when the caller omits them.\n\n```python\ndef connect(host, *, timeout=5, secure=True):\n scheme = "https" if secure else "http"\n print(f"{scheme}://{host} timeout={timeout}")\n\nconnect("example.com")\n```\n\n```output\nhttps://example.com timeout=5\n```\n:::\n\n:::cell\nNaming the option makes the call site explicit. A reader does not have to remember which positional slot controls the timeout.\n\n```python\nconnect("example.com", timeout=10)\n```\n\n```output\nhttps://example.com timeout=10\n```\n:::\n\n:::cell\nFlags are especially good keyword-only arguments because a bare positional `False` is hard to interpret.\n\n```python\nconnect("localhost", secure=False)\n```\n\n```output\nhttp://localhost timeout=5\n```\n:::\n\n:::cell\nThe bare `*` is enforced at the call site: passing the timeout positionally raises `TypeError` instead of silently filling the wrong slot.\n\n```python\ntry:\n connect("example.com", 10)\nexcept TypeError as error:\n print(type(error).__name__)\n```\n\n```output\nTypeError\n```\n:::\n\n:::note\n- Put `*` before options that callers should name.\n- Keyword-only flags avoid mysterious positional `True` and `False` arguments.\n- Defaults work normally for keyword-only parameters.\n:::\n', 'lambdas.md': '+++\nslug = "lambdas"\ntitle = "Lambdas"\nsection = "Functions"\nsummary = "lambda creates small anonymous function expressions."\ndoc_path = "/tutorial/controlflow.html#lambda-expressions"\nsee_also = [\n "functions",\n "sorting",\n "callable-objects",\n]\n+++\n\n`lambda` creates a small anonymous function expression. It is most useful when Python asks for a function and the behavior is short enough to read inline.\n\nA lambda can only contain one expression. Use `def` when the behavior deserves a name, needs statements, or would be easier to test separately.\n\nLambdas often appear as key functions, callbacks, and tiny adapters. Keep them simple enough that the call site remains clearer than a named helper.\n\n:::program\n```python\nadd_tax = lambda price: round(price * 1.08, 2)\nprint(add_tax(10))\n\nitems = [("notebook", 5), ("pen", 2), ("bag", 20)]\nby_price = sorted(items, key=lambda item: item[1])\nprint(by_price)\n\ndef price(item):\n return item[1]\n\nprint(sorted(items, key=price))\n```\n:::\n\n:::cell\nA lambda is a function expression. Assigning one to a name works, although `def` is usually clearer for reusable behavior.\n\n```python\nadd_tax = lambda price: round(price * 1.08, 2)\nprint(add_tax(10))\n```\n\n```output\n10.8\n```\n:::\n\n:::cell\nLambdas are most idiomatic when passed directly to another function. `sorted()` calls this key function once for each item.\n\n```python\nitems = [("notebook", 5), ("pen", 2), ("bag", 20)]\nby_price = sorted(items, key=lambda item: item[1])\nprint(by_price)\n```\n\n```output\n[(\'pen\', 2), (\'notebook\', 5), (\'bag\', 20)]\n```\n:::\n\n:::cell\nA named function is better when the behavior should be reused or explained. It produces the same sort key, but gives the operation a name.\n\n```python\ndef price(item):\n return item[1]\n\nprint(sorted(items, key=price))\n```\n\n```output\n[(\'pen\', 2), (\'notebook\', 5), (\'bag\', 20)]\n```\n:::\n\n:::note\n- Lambdas are expressions, not statements.\n- Prefer `def` for multi-step or reused behavior.\n- Lambdas are common as `key=` functions because the behavior is local to one call.\n:::\n', 'lists.md': '+++\nslug = "lists"\ntitle = "Lists"\nsection = "Collections"\nsummary = "Lists are ordered, mutable collections."\ndoc_path = "/tutorial/datastructures.html#more-on-lists"\nsee_also = [\n "tuples",\n "sets",\n "slices",\n "copying-collections",\n]\n+++\n\nLists are Python\'s general-purpose mutable sequence type. Use them when order matters and the collection may grow, shrink, or be rearranged.\n\nIndexing reads individual positions. `0` is the first item, and negative indexes count backward from the end.\n\nMutation and copying matter: `append()` changes the list, while `sorted()` returns a new ordered list and leaves the original alone.\n\n:::program\n```python\nnumbers = [3, 1, 4]\nnumbers.append(1)\n\nprint(numbers)\nprint(numbers[0])\nprint(numbers[-1])\nprint(sorted(numbers))\nprint(numbers)\n```\n:::\n\n:::cell\nCreate a list with square brackets. Because lists are mutable, `append()` changes this same list object.\n\n```python\nnumbers = [3, 1, 4]\nnumbers.append(1)\n\nprint(numbers)\n```\n\n```output\n[3, 1, 4, 1]\n```\n:::\n\n:::cell\nUse indexes to read positions. Negative indexes are convenient for reading from the end.\n\n```python\nprint(numbers[0])\nprint(numbers[-1])\n```\n\n```output\n3\n1\n```\n:::\n\n:::cell\nUse `sorted()` when you want an ordered copy and still need the original order afterward.\n\n```python\nprint(sorted(numbers))\nprint(numbers)\n```\n\n```output\n[1, 1, 3, 4]\n[3, 1, 4, 1]\n```\n:::\n\n:::note\n- Lists are mutable sequences: methods such as `append()` change the list in place.\n- Negative indexes count from the end.\n- `sorted()` returns a new list; `list.sort()` sorts the existing list in place.\n:::\n', 'literal-and-final.md': '+++\nslug = "literal-and-final"\ntitle = "Literal and Final"\nsection = "Types"\nsummary = "Literal restricts exact values, while Final marks names that should not be rebound."\ndoc_path = "/library/typing.html#typing.Literal"\nsee_also = [\n "type-hints",\n "constants",\n "union-and-optional-types",\n "overloads",\n]\n+++\n\n`Literal` and `Final` make two different static promises. `Literal` narrows a value to exact allowed options. `Final` tells a type checker that a name should not be rebound after its first assignment.\n\nBoth annotations help at API boundaries: a function can accept only known modes, and a module can publish a constant that other code should treat as fixed. Python still runs the same assignment rules at runtime, so these are promises for tools and readers rather than runtime locks.\n\nUse `Literal` when a small closed set is clearer than a broad `str` or `int`. Use `Final` when rebinding would be a bug in the design, especially for module constants and class attributes.\n\n:::program\n```python\nfrom typing import Final, Literal\n\nMode = Literal["read", "write"]\nDEFAULT_MODE: Final[Mode] = "read"\n\n\ndef open_label(mode: Mode) -> str:\n return f"opening for {mode}"\n\nprint(open_label(DEFAULT_MODE))\nprint(open_label("write"))\n\nDEFAULT_MODE = "debug"\nprint(DEFAULT_MODE)\n```\n:::\n\n:::cell\n`Literal` describes exact allowed values. A type checker can reject `"debug"` as a `Mode` even though it is an ordinary string at runtime.\n\n```python\nfrom typing import Final, Literal\n\nMode = Literal["read", "write"]\n\n\ndef open_label(mode: Mode) -> str:\n return f"opening for {mode}"\n\nprint(open_label("write"))\n```\n\n```output\nopening for write\n```\n:::\n\n:::cell\n`Final` marks a name that should not be rebound. It is stronger documentation than the all-caps constant convention because static tools can flag reassignment.\n\n```python\nDEFAULT_MODE: Final[Mode] = "read"\nprint(open_label(DEFAULT_MODE))\n```\n\n```output\nopening for read\n```\n:::\n\n:::cell\nThe annotation is not a runtime lock. Python still rebinds the name; the mistake is that a type checker and human reader should reject the design.\n\n```python\nDEFAULT_MODE = "debug"\nprint(DEFAULT_MODE)\n```\n\n```output\ndebug\n```\n:::\n\n:::note\n- `Literal` narrows values to a small exact set.\n- `Final` prevents rebinding in static analysis, not at runtime.\n- Use enums when the option set needs names, behavior, or iteration over members.\n:::\n', 'literals.md': '+++\nslug = "literals"\ntitle = "Literals"\nsection = "Basics"\nsummary = "Literals write values directly in Python source code."\ndoc_path = "/reference/lexical_analysis.html#literals"\nsee_also = [\n "values",\n "strings",\n "numbers",\n "string-formatting",\n]\n+++\n\nLiterals are source-code forms for values: numbers, text, bytes, containers, booleans, `None`, and a few specialized markers. They are how a program writes small values directly.\n\nThe literal form is only the beginning. Later examples explain each value family in depth: strings are Unicode text, bytes are binary data, lists and dicts are containers, and `None` represents intentional absence.\n\nUse literals when the value is small and local. Give repeated or meaningful values a name so the program explains why that value matters.\n\n:::program\n```python\nwhole = 42\nfraction = 3.5\ncomplex_number = 2 + 3j\nprint(whole, fraction, complex_number.imag)\n\nflags = 0xFF\nmask = 0b1010\nmillion = 1_000_000\nprint(flags, mask, million)\n\ntext = "python"\nraw_pattern = r"\\d+"\ndata = b"py"\nscore = 98\nformatted = f"score={score}"\nprint(text)\nprint(raw_pattern)\nprint(data)\nprint(formatted)\n\npoint = (2, 3)\nnames = ["Ada", "Grace"]\nscores = {"Ada": 98}\nunique = {"py", "go"}\nprint(point)\nprint(names[0])\nprint(scores["Ada"])\nprint(sorted(unique))\n\nprint(True, False, None)\nprint(...)\n\nprint(type({}).__name__)\nprint(type(set()).__name__)\nprint(type({1, 2}).__name__)\n```\n:::\n\n:::cell\nNumeric literals write numbers directly. Complex literals use `j` for the imaginary part.\n\n```python\nwhole = 42\nfraction = 3.5\ncomplex_number = 2 + 3j\nprint(whole, fraction, complex_number.imag)\n```\n\n```output\n42 3.5 3.0\n```\n:::\n\n:::cell\nInteger literals also accept hexadecimal (`0x`), binary (`0b`), and octal (`0o`) prefixes. Underscores group digits visually without changing the value.\n\n```python\nflags = 0xFF\nmask = 0b1010\nmillion = 1_000_000\nprint(flags, mask, million)\n```\n\n```output\n255 10 1000000\n```\n:::\n\n:::cell\nString literals write Unicode text. Raw strings keep backslashes literal, bytes literals write binary data rather than text, and f-strings (`f"..."`) embed expressions inline.\n\n```python\ntext = "python"\nraw_pattern = r"\\d+"\ndata = b"py"\nscore = 98\nformatted = f"score={score}"\nprint(text)\nprint(raw_pattern)\nprint(data)\nprint(formatted)\n```\n\n```output\npython\n\\d+\nb\'py\'\nscore=98\n```\n:::\n\n:::cell\nContainer literals create tuples, lists, dictionaries, and sets. Each container answers a different question about order, position, lookup, or uniqueness.\n\n```python\npoint = (2, 3)\nnames = ["Ada", "Grace"]\nscores = {"Ada": 98}\nunique = {"py", "go"}\nprint(point)\nprint(names[0])\nprint(scores["Ada"])\nprint(sorted(unique))\n```\n\n```output\n(2, 3)\nAda\n98\n[\'go\', \'py\']\n```\n:::\n\n:::cell\n`True`, `False`, `None`, and `...` are singleton literal-like constants used for truth values, absence, and placeholders.\n\n```python\nprint(True, False, None)\nprint(...)\n```\n\n```output\nTrue False None\nEllipsis\n```\n:::\n\n:::cell\nCurly-brace literals are dictionaries by default. The empty form `{}` is an empty dictionary, not an empty set; use `set()` for that. A non-empty `{1, 2}` is a set because keyless items can only be a set.\n\n```python\nprint(type({}).__name__)\nprint(type(set()).__name__)\nprint(type({1, 2}).__name__)\n```\n\n```output\ndict\nset\nset\n```\n:::\n\n:::note\n- Literals are good for small local values; constants are better for repeated values with meaning.\n- `{}` is an empty dictionary. Use `set()` for an empty set.\n- Bytes literals are binary data; string literals are Unicode text.\n- `...` evaluates to the `Ellipsis` object.\n:::\n', 'logging.md': '+++\nslug = "logging"\ntitle = "Logging"\nsection = "Standard Library"\nsummary = "logging records operational events without using print as infrastructure."\ndoc_path = "/library/logging.html"\nsee_also = [\n "exceptions",\n "testing",\n "modules",\n]\n+++\n\n`logging` records operational events without using `print` as infrastructure. A logger names where an event came from, a handler decides where records go, a formatter chooses their text shape, and a level decides which records are important enough to emit.\n\nUse logging for services, command-line tools, scheduled jobs, and libraries that need diagnostics operators can filter. Use `print` for a program\'s intentional user-facing output.\n\nThe example writes to stdout so the page stays deterministic. Real applications usually configure handlers once at startup and then call `logging.getLogger(__name__)` from each module.\n\n:::program\n```python\nimport logging\nimport sys\n\nlogger = logging.getLogger("example.worker")\nlogger.setLevel(logging.DEBUG)\nhandler = logging.StreamHandler(sys.stdout)\nhandler.setLevel(logging.INFO)\nparts = ["%(levelname)s", "%(name)s", "%(message)s"]\nformatter = logging.Formatter(":".join(parts))\nhandler.setFormatter(formatter)\nlogger.handlers[:] = [handler]\nlogger.propagate = False\n\nlogger.debug("hidden detail")\nlogger.info("service started")\nlogger.warning("disk almost full")\n\nhandler.setLevel(logging.WARNING)\nlogger.info("hidden after threshold change")\nlogger.error("write failed")\n```\n:::\n\n:::cell\nA logger name records which part of the program produced the event. The handler and formatter choose where and how the event is shown.\n\n```python\nimport logging\nimport sys\n\nlogger = logging.getLogger("example.worker")\nlogger.setLevel(logging.DEBUG)\nhandler = logging.StreamHandler(sys.stdout)\nhandler.setLevel(logging.INFO)\nparts = ["%(levelname)s", "%(name)s", "%(message)s"]\nformatter = logging.Formatter(":".join(parts))\nhandler.setFormatter(formatter)\nlogger.handlers[:] = [handler]\nlogger.propagate = False\n\nlogger.debug("hidden detail")\nlogger.info("service started")\nlogger.warning("disk almost full")\n```\n\n```output\nINFO:example.worker:service started\nWARNING:example.worker:disk almost full\n```\n:::\n\n:::cell\nLevels are thresholds. Raising the handler level to `WARNING` suppresses later `INFO` records without changing the call sites.\n\n```python\nimport logging\nimport sys\n\nlogger = logging.getLogger("example.worker")\nlogger.setLevel(logging.DEBUG)\nhandler = logging.StreamHandler(sys.stdout)\nhandler.setLevel(logging.WARNING)\nparts = ["%(levelname)s", "%(name)s", "%(message)s"]\nformatter = logging.Formatter(":".join(parts))\nhandler.setFormatter(formatter)\nlogger.handlers[:] = [handler]\nlogger.propagate = False\n\nlogger.info("hidden after threshold change")\nlogger.error("write failed")\n```\n\n```output\nERROR:example.worker:write failed\n```\n:::\n\n:::note\n- Configure logging once; call named loggers throughout the program.\n- Logger and handler levels both participate in filtering.\n- Use exceptions for control flow failures, logging for operational evidence, and warnings for soft compatibility problems.\n:::\n', 'loop-else.md': '+++\nslug = "loop-else"\ntitle = "Loop Else"\nsection = "Control Flow"\nsummary = "A loop else block runs only when the loop did not end with break."\ndoc_path = "/tutorial/controlflow.html#else-clauses-on-loops"\nsee_also = [\n "break-and-continue",\n "for-loops",\n "while-loops",\n]\n+++\n\nPython loops can have an `else` clause. The name is surprising at first: loop `else` means “no `break` happened,” not “the loop condition was false.”\n\nThis is useful for searches. Put the successful early exit in `break`, then put the not-found path in `else`.\n\nUse loop `else` sparingly. It is clearest when the loop is visibly searching for something.\n\n:::program\n```python\nnames = ["Ada", "Grace", "Guido"]\n\nfor name in names:\n if name == "Grace":\n print("found")\n break\nelse:\n print("missing")\n\nfor name in names:\n if name == "Linus":\n print("found")\n break\nelse:\n print("missing")\n```\n:::\n\n:::cell\nIf the loop reaches `break`, the `else` block is skipped. This branch means the search succeeded early.\n\n```python\nnames = ["Ada", "Grace", "Guido"]\n\nfor name in names:\n if name == "Grace":\n print("found")\n break\nelse:\n print("missing")\n```\n\n```output\nfound\n```\n:::\n\n:::cell\nIf the loop finishes without `break`, the `else` block runs. This branch means the search examined every value and found nothing.\n\n```python\nfor name in names:\n if name == "Linus":\n print("found")\n break\nelse:\n print("missing")\n```\n\n```output\nmissing\n```\n:::\n\n:::note\n- Loop `else` runs when the loop was not ended by `break`.\n- It is best for search loops with a clear found/not-found split.\n- It works with both `for` and `while` loops.\n:::\n', 'match-statements.md': '+++\nslug = "match-statements"\ntitle = "Match Statements"\nsection = "Control Flow"\nsummary = "match selects cases using structural pattern matching."\ndoc_path = "/tutorial/controlflow.html#match-statements"\nsee_also = [\n "conditionals",\n "advanced-match-patterns",\n "structured-data-shapes",\n "dicts",\n]\n+++\n\nStructural pattern matching lets a program choose a branch based on the shape of data. It is especially useful when commands, messages, or parsed data have a few known forms.\n\nA `case` pattern can both check constants and bind names. The move case checks the action and extracts `x` and `y` in one readable step.\n\nOrder matters because Python tries cases from top to bottom. Specific shapes should appear before broad fallback cases such as `_`.\n\n:::program\n```python\ncommand = {"action": "move", "x": 3, "y": 4}\n\nmatch command:\n case {"action": "move", "x": x, "y": y}:\n print(f"move to {x},{y}")\n case {"action": "quit"}:\n print("quit")\n case {"action": action}:\n print(f"unknown action: {action}")\n case _:\n print("invalid command")\n```\n:::\n\n:::cell\nUse `match` when the shape of a value is the decision. This command is a dictionary with an action and coordinates; the first case checks that shape and binds `x` and `y`.\n\n```python\ncommand = {"action": "move", "x": 3, "y": 4}\n\nmatch command:\n case {"action": "move", "x": x, "y": y}:\n print(f"move to {x},{y}")\n```\n\n```output\nmove to 3,4\n```\n:::\n\n:::cell\nOther cases describe other valid shapes. This complete fragment changes the command so the `quit` case is the first matching pattern.\n\n```python\ncommand = {"action": "quit"}\n\nmatch command:\n case {"action": "move", "x": x, "y": y}:\n print(f"move to {x},{y}")\n case {"action": "quit"}:\n print("quit")\n```\n\n```output\nquit\n```\n:::\n\n:::cell\nBroader patterns and the `_` catch-all belong after specific cases. This fragment extracts an unknown action before the final fallback would run.\n\n```python\ncommand = {"action": "jump"}\n\nmatch command:\n case {"action": "move", "x": x, "y": y}:\n print(f"move to {x},{y}")\n case {"action": "quit"}:\n print("quit")\n case {"action": action}:\n print(f"unknown action: {action}")\n case _:\n print("invalid command")\n```\n\n```output\nunknown action: jump\n```\n:::\n\n:::note\n- `match` compares structure, not just equality.\n- Patterns can bind names such as `x` and `y` while matching.\n- Mapping patterns match when the named keys are present; extra keys in the subject are ignored rather than failing the case.\n- Put the catch-all `_` case last, because cases are tried from top to bottom.\n:::\n', 'metaclasses.md': '+++\nslug = "metaclasses"\ntitle = "Metaclasses"\nsection = "Classes"\nsummary = "A metaclass customizes how classes themselves are created."\ndoc_path = "/reference/datamodel.html#metaclasses"\nsee_also = [\n "classes",\n "inheritance-and-super",\n "special-methods",\n]\n+++\n\nA metaclass is the class of a class. Most Python code never needs one, but the syntax appears in frameworks that register, validate, or modify classes as they are created.\n\nThe `metaclass=` keyword in a class statement chooses the object that builds the class. This is advanced machinery; decorators and ordinary functions are usually simpler.\n\nUse metaclasses only when class creation itself is the problem being solved.\n\n:::program\n```python\nclass Tagged(type):\n def __new__(mcls, name, bases, namespace):\n namespace["tag"] = name.lower()\n return super().__new__(mcls, name, bases, namespace)\n\nclass Event(metaclass=Tagged):\n pass\n\nprint(Event.tag)\nprint(type(Event).__name__)\n```\n:::\n\n:::cell\nA metaclass customizes class creation. `__new__` receives the class name, bases, and namespace before the class object exists.\n\n```python\nclass Tagged(type):\n def __new__(mcls, name, bases, namespace):\n namespace["tag"] = name.lower()\n return super().__new__(mcls, name, bases, namespace)\n\nprint(Tagged.__name__)\n```\n\n```output\nTagged\n```\n:::\n\n:::cell\nThe `metaclass=` keyword applies that class-building rule. Here the metaclass adds a `tag` attribute to the new class.\n\n```python\nclass Event(metaclass=Tagged):\n pass\n\nprint(Event.tag)\nprint(type(Event).__name__)\n```\n\n```output\nevent\nTagged\n```\n:::\n\n:::note\n- Metaclasses customize class creation, not instance behavior directly.\n- Most code should prefer class decorators, functions, or ordinary inheritance.\n- You are most likely to meet metaclasses inside frameworks and ORMs.\n:::\n', 'modules.md': '+++\nslug = "modules"\ntitle = "Modules"\nsection = "Modules"\nsummary = "Modules organize code into namespaces and expose reusable definitions."\ndoc_path = "/tutorial/modules.html"\nsee_also = [\n "import-aliases",\n "packages",\n]\n+++\n\nModules organize Python code into files and namespaces. `import` executes a module once, stores it in Python\'s import cache, and gives your program access to its definitions.\n\nThis page focuses on import forms and module namespaces. Package layout, aliases, and dynamic imports have their own neighboring examples.\n\nUse module namespaces such as `math.sqrt` when the source of a name should stay visible. Use focused imports such as `from statistics import mean` when the imported name is clear at the call site.\n\n:::program\n```python\nimport math\nimport sys\nfrom statistics import mean\n\nradius = 3\narea = math.pi * radius ** 2\nprint(round(area, 2))\n\nscores = [8, 10, 9]\nprint(mean(scores))\n\nprint(math.__name__)\nprint("math" in sys.modules)\n```\n:::\n\n:::cell\nImporting a module gives access to its namespace. The `math.` prefix makes it clear where `pi` came from.\n\n```python\nimport math\n\nradius = 3\narea = math.pi * radius ** 2\nprint(round(area, 2))\n```\n\n```output\n28.27\n```\n:::\n\n:::cell\nA focused `from ... import ...` brings one definition into the current namespace. This keeps a common operation concise without importing every name.\n\n```python\nfrom statistics import mean\n\nscores = [8, 10, 9]\nprint(mean(scores))\n```\n\n```output\n9\n```\n:::\n\n:::cell\nModules are objects too. Their attributes include metadata such as `__name__`, which records the module\'s import name.\n\n```python\nprint(math.__name__)\n```\n\n```output\nmath\n```\n:::\n\n:::cell\nImported modules are cached in `sys.modules`. Later imports reuse the module object instead of executing the file again.\n\n```python\nimport sys\nprint("math" in sys.modules)\n```\n\n```output\nTrue\n```\n:::\n\n:::note\n- Prefer plain `import module` when the namespace improves readability.\n- Use focused imports for a small number of clear names.\n- Place imports near the top of the file.\n- Imports execute module top-level code once, then reuse the cached module object.\n:::\n', 'multiple-return-values.md': '+++\nslug = "multiple-return-values"\ntitle = "Multiple Return Values"\nsection = "Functions"\nsummary = "Python returns multiple values by returning a tuple and unpacking it."\ndoc_path = "/tutorial/datastructures.html#tuples-and-sequences"\nsee_also = [\n "tuples",\n "unpacking",\n "functions",\n]\n+++\n\nPython multiple return values are tuple return values with friendly syntax. `return a, b` creates one tuple containing two positions.\n\nMost callers unpack that tuple immediately. Good target names make the meaning of each returned position explicit.\n\nUse this for small, fixed groups of results. For larger records, a dataclass or named tuple usually communicates better.\n\n:::program\n```python\ndef divide_with_remainder(total, size):\n quotient = total // size\n remainder = total % size\n return quotient, remainder\n\nresult = divide_with_remainder(17, 5)\nprint(result)\n\nboxes, leftover = result\nprint(boxes)\nprint(leftover)\n```\n:::\n\n:::cell\nReturning values separated by commas returns one tuple. The tuple is visible if the caller stores the result directly.\n\n```python\ndef divide_with_remainder(total, size):\n quotient = total // size\n remainder = total % size\n return quotient, remainder\n\nresult = divide_with_remainder(17, 5)\nprint(result)\n```\n\n```output\n(3, 2)\n```\n:::\n\n:::cell\nCallers usually unpack the tuple immediately or soon after. The names at the call site document what each position means.\n\n```python\nboxes, leftover = result\nprint(boxes)\nprint(leftover)\n```\n\n```output\n3\n2\n```\n:::\n\n:::note\n- A comma creates a tuple; `return a, b` returns one tuple containing two values.\n- Unpacking at the call site gives each returned position a meaningful name.\n- Use a class-like record when the result has many fields.\n:::\n', 'mutability.md': '+++\nslug = "mutability"\ntitle = "Mutability"\nsection = "Data Model"\nsummary = "Some objects change in place, while others return new values."\ndoc_path = "/reference/datamodel.html#objects-values-and-types"\nsee_also = [\n "variables",\n "object-lifecycle",\n "copying-collections",\n "lists",\n]\n+++\n\nObjects in Python can be mutable or immutable. Mutable objects such as lists and dictionaries can change in place, while immutable objects such as strings and tuples produce new values instead.\n\nNames can share one mutable object, so a change through one name is visible through another. This is powerful, but it is also the source of many beginner surprises.\n\nThe boundary matters across Python: `append()` mutates a list, string methods return new strings, and `sorted()` returns a new list while `list.sort()` mutates an existing one.\n\n:::program\n```python\nfirst = ["python"]\nsecond = first\nsecond.append("workers")\nprint(first)\nprint(second)\n\ntext = "python"\nupper_text = text.upper()\nprint(text)\nprint(upper_text)\n\nnumbers = [3, 1, 2]\nordered = sorted(numbers)\nprint(ordered)\nprint(numbers)\n```\n:::\n\n:::cell\nMutable objects can change in place. `first` and `second` point to the same list, so appending through one name changes the object seen through both names.\n\n```python\nfirst = ["python"]\nsecond = first\nsecond.append("workers")\nprint(first)\nprint(second)\n```\n\n```output\n[\'python\', \'workers\']\n[\'python\', \'workers\']\n```\n:::\n\n:::cell\nImmutable objects do not change in place. String methods such as `upper()` return a new string, leaving the original string unchanged.\n\n```python\ntext = "python"\nupper_text = text.upper()\nprint(text)\nprint(upper_text)\n```\n\n```output\npython\nPYTHON\n```\n:::\n\n:::cell\nSome APIs make the boundary explicit. `sorted()` returns a new list, while methods such as `append()` and `list.sort()` mutate an existing list.\n\n```python\nnumbers = [3, 1, 2]\nordered = sorted(numbers)\nprint(ordered)\nprint(numbers)\n```\n\n```output\n[1, 2, 3]\n[3, 1, 2]\n```\n:::\n\n:::note\n- Lists and dictionaries are mutable; strings and tuples are immutable.\n- Aliasing is useful, but copy mutable containers when independent changes are needed.\n- Pay attention to whether an operation mutates in place or returns a new value.\n:::\n', 'networking.md': '+++\nslug = "networking"\ntitle = "Networking"\nsection = "Standard Library"\nsummary = "Networking code exchanges bytes across explicit protocol boundaries."\ndoc_path = "/library/socket.html"\nsee_also = [\n "bytes-and-bytearray",\n "subprocesses",\n "async-await",\n]\nexpected_output = "b\'ping\'\\nping\\n"\n+++\n\nNetworking code sends and receives bytes across protocol boundaries. Higher-level HTTP clients hide many details, but the core rule remains: text is encoded before it leaves the process and decoded after bytes come back.\n\nIn standard Python, the socket version of this lesson uses connected endpoints such as `socket.create_connection()` or, for a local deterministic demonstration, `socket.socketpair()`. This site\'s live example runner does not expose arbitrary OS sockets or outbound calls, so this page teaches the socket contract while making the runner constraint explicit.\n\nThe useful mental model is endpoint plus bytes plus cleanup. A socket connects two endpoints, transfers byte strings, and must be closed when the conversation is finished.\n\n:::program\n```python\nimport socket\n\nleft, right = socket.socketpair()\ntry:\n message = "ping"\n left.sendall(message.encode("utf-8"))\n data = right.recv(16)\n print(data)\n print(data.decode("utf-8"))\nfinally:\n left.close()\n right.close()\n```\n:::\n\n:::cell\n`socketpair()` returns two connected endpoints. `sendall` writes encoded bytes into one end and `recv` reads up to 16 bytes off the other — the byte boundary is the whole point: `"ping".encode("utf-8")` produces `b\'ping\'`, which is what the socket actually moves. The `try`/`finally` closes both endpoints even if `recv` raises, and the second `print` `decode`s the bytes back into a Python `str`. The in-browser sandbox cannot open sockets, so pressing Run here fails; this output came from a real socket pair under standard CPython at build time.\n\n```python\nimport socket\n\nleft, right = socket.socketpair()\ntry:\n message = "ping"\n left.sendall(message.encode("utf-8"))\n data = right.recv(16)\n print(data)\n print(data.decode("utf-8"))\nfinally:\n left.close()\n right.close()\n```\n\n```output\nb\'ping\'\nping\n```\n:::\n\n:::note\n- Network protocols move bytes, not Python `str` objects.\n- Close real sockets when finished, usually with a context manager or `finally` block.\n- Use high-level HTTP libraries for application HTTP unless socket-level control is the lesson.\n- The verified output came from a real `socketpair()` under standard CPython at build time; the in-browser sandbox cannot open sockets, so live runs of this page fail there.\n:::\n', 'newtype.md': '+++\nslug = "newtype"\ntitle = "NewType"\nsection = "Types"\nsummary = "NewType creates distinct static identities for runtime-compatible values."\ndoc_path = "/library/typing.html#typing.NewType"\nsee_also = [\n "type-aliases",\n "type-hints",\n "runtime-type-checks",\n]\n+++\n\n`NewType` creates a distinct static identity for a value that is represented by an existing runtime type. It is useful for IDs, units, and other values that should not be mixed accidentally.\n\nThe key boundary is static versus runtime behavior. A type checker can distinguish `UserId` from `OrderId`, but at runtime both values are plain integers.\n\nUse a type alias when you only want a clearer name for a shape. Use `NewType` when mixing two compatible shapes should be treated as a mistake by static analysis.\n\n:::program\n```python\nfrom typing import NewType\n\nUserId = NewType("UserId", int)\nOrderId = NewType("OrderId", int)\n\n\ndef load_user(user_id: UserId) -> str:\n return f"user {user_id}"\n\nuid = UserId(42)\noid = OrderId(42)\nprint(load_user(uid))\nprint(uid == oid)\nprint(type(uid).__name__)\nprint(UserId.__name__)\n```\n:::\n\n:::cell\n`NewType` helps type checkers distinguish values that share a runtime representation.\n\n```python\nfrom typing import NewType\n\nUserId = NewType("UserId", int)\nOrderId = NewType("OrderId", int)\n\n\ndef load_user(user_id: UserId) -> str:\n return f"user {user_id}"\n\nuid = UserId(42)\nprint(load_user(uid))\n```\n\n```output\nuser 42\n```\n:::\n\n:::cell\nAt runtime, a `NewType` value is the underlying value. It compares like that value and has the same runtime type.\n\n```python\noid = OrderId(42)\nprint(uid == oid)\nprint(type(uid).__name__)\n```\n\n```output\nTrue\nint\n```\n:::\n\n:::cell\nThe `NewType` constructor keeps a name for static tools and introspection.\n\n```python\nprint(UserId.__name__)\nprint(OrderId.__name__)\n```\n\n```output\nUserId\nOrderId\n```\n:::\n\n:::note\n- `NewType` helps type checkers distinguish values that share a runtime representation.\n- At runtime, the value is still the underlying type.\n- Use aliases for readability; use `NewType` for static separation.\n:::\n', 'none.md': '+++\nslug = "none"\ntitle = "None"\nsection = "Basics"\nsummary = "None represents expected absence, distinct from missing keys and errors."\ndoc_path = "/library/constants.html#None"\nsee_also = [\n "values",\n "truthiness",\n "exceptions",\n "dicts",\n]\n+++\n\n`None` represents the absence of a value. It is the usual sentinel when a function has no result to return but the absence itself is meaningful.\n\nBecause `None` is a singleton, idiomatic Python checks it with `is None` or `is not None`. This avoids confusing identity with value equality.\n\nAbsence has several nearby shapes in Python. A function can return `None`, a dictionary lookup can supply a default for a missing key, and an invalid operation can raise an exception.\n\n:::program\n```python\nresult = None\nprint(result is None)\n\ndef find_score(name):\n if name == "Ada":\n return 10\n return None\n\nscore = find_score("Grace")\nprint(score is None)\n\nprofile = {"name": "Ada"}\nprint(profile.get("timezone", "UTC"))\n\ntry:\n int("python")\nexcept ValueError:\n print("invalid number")\n```\n:::\n\n:::cell\n`None` is Python\'s value for “nothing here.” Check it with `is None` because it is a singleton identity value.\n\n```python\nresult = None\nprint(result is None)\n```\n\n```output\nTrue\n```\n:::\n\n:::cell\nFunctions often return `None` when absence is expected and callers can continue. The function name and surrounding code should make that possibility clear.\n\n```python\ndef find_score(name):\n if name == "Ada":\n return 10\n return None\n\nscore = find_score("Grace")\nprint(score is None)\n```\n\n```output\nTrue\n```\n:::\n\n:::cell\nA missing dictionary key is another absence boundary. Use `get()` when the mapping can supply a default, and use exceptions for invalid operations that cannot produce a value.\n\n```python\nprofile = {"name": "Ada"}\nprint(profile.get("timezone", "UTC"))\n\ntry:\n int("python")\nexcept ValueError:\n print("invalid number")\n```\n\n```output\nUTC\ninvalid number\n```\n:::\n\n:::note\n- Use `is None` rather than `== None`; `None` is a singleton identity value.\n- Use `None` for expected absence that callers can test.\n- Use dictionary defaults for missing mapping keys and exceptions for invalid operations.\n:::\n', 'number-parsing.md': '+++\nslug = "number-parsing"\ntitle = "Number Parsing"\nsection = "Standard Library"\nsummary = "int() and float() parse text into numbers and raise ValueError on bad input."\ndoc_path = "/library/functions.html#int"\nsee_also = [\n "exceptions",\n "strings",\n "numbers",\n]\n+++\n\nParsing turns text from files, forms, command lines, or network messages into numeric objects. `int()` parses whole-number text, and `float()` parses decimal or scientific-notation text.\n\nInvalid numeric text raises `ValueError`. Catch that specific exception when bad user input is expected and recoverable; let it fail loudly when the string is supposed to be trusted program data.\n\n`int()` also accepts a base, which is useful at protocol boundaries where numbers are written in hexadecimal, binary, or another explicit notation.\n\n:::program\n```python\nprint(int("42"))\nprint(float("3.5"))\nprint(int("ff", 16))\n\ntexts = ["10", "python", "20"]\nfor text in texts:\n try:\n print(int(text) * 2)\n except ValueError:\n print(f"skip {text!r}")\n```\n:::\n\n:::cell\nUse `int()` for whole numbers and `float()` for decimal text. Parsed values are real numbers, not strings.\n\n```python\nprint(int("42"))\nprint(float("3.5"))\n```\n\n```output\n42\n3.5\n```\n:::\n\n:::cell\nPass a base when the text format says the number is not decimal.\n\n```python\nprint(int("ff", 16))\n```\n\n```output\n255\n```\n:::\n\n:::cell\nCatch `ValueError` at the input boundary when invalid text is normal and recoverable.\n\n```python\ntexts = ["10", "python", "20"]\nfor text in texts:\n try:\n print(int(text) * 2)\n except ValueError:\n print(f"skip {text!r}")\n```\n\n```output\n20\nskip \'python\'\n40\n```\n:::\n\n:::note\n- `int()` and `float()` are constructors that also parse strings.\n- `int(text, base)` makes non-decimal input explicit.\n- Catch `ValueError` for recoverable user input; do not hide unexpected data corruption.\n:::\n', 'numbers.md': '+++\nslug = "numbers"\ntitle = "Numbers"\nsection = "Basics"\nsummary = "Python numbers include integers, floats, and complex values."\ndoc_path = "/library/stdtypes.html#numeric-types-int-float-complex"\nsee_also = [\n "literals",\n "operators",\n]\n+++\n\nPython\'s numeric model starts with `int`, `float`, and `complex`. Integers are arbitrary precision, floats are approximate double-precision values, and complex numbers carry real and imaginary parts.\n\nOperators encode different numeric questions. `/` means true division and returns a float, `//` means floor division, `%` gives the remainder, and `**` computes powers.\n\nUse rounding for display, not as a substitute for understanding floating-point approximation. Financial code usually needs `decimal.Decimal`, which is a separate precision topic.\n\n:::program\n```python\nimport math\n\ncount = 10\nratio = 0.25\nz = 2 + 3j\n\nprint(count + 5)\nprint(count / 4)\nprint(ratio * 2)\nprint(count // 4)\nprint(count % 4)\nprint(2 ** 5)\nprint(z.real, z.imag)\nprint(0.1 + 0.2)\nprint(0.1 + 0.2 == 0.3)\nprint(math.isclose(0.1 + 0.2, 0.3))\nprint(round(3.14159, 2))\n```\n:::\n\n:::cell\nPython has `int` for whole numbers and `float` for approximate real-valued arithmetic. True division with `/` returns a `float`, even when both inputs are integers.\n\n```python\ncount = 10\nratio = 0.25\n\nprint(count + 5)\nprint(count / 4)\nprint(ratio * 2)\n```\n\n```output\n15\n2.5\n0.5\n```\n:::\n\n:::cell\nFloor division and modulo are useful when you need quotient and remainder behavior. Powers use `**`, not `^`.\n\n```python\nprint(count // 4)\nprint(count % 4)\nprint(2 ** 5)\n```\n\n```output\n2\n2\n32\n```\n:::\n\n:::cell\nComplex numbers are built in. The literal suffix `j` marks the imaginary part.\n\n```python\nz = 2 + 3j\nprint(z.real, z.imag)\n```\n\n```output\n2.0 3.0\n```\n:::\n\n:::cell\nFloating-point values are approximate, so `==` between expected and computed floats is rarely the right test. Compare with `math.isclose` (or work in `decimal.Decimal`) when the question is "are these the same number to within tolerance".\n\n```python\nimport math\n\nprint(0.1 + 0.2)\nprint(0.1 + 0.2 == 0.3)\nprint(math.isclose(0.1 + 0.2, 0.3))\nprint(round(3.14159, 2))\n```\n\n```output\n0.30000000000000004\nFalse\nTrue\n3.14\n```\n:::\n\n:::note\n- Python\'s `int` has arbitrary precision; it grows as large as memory allows.\n- Python\'s `float` is approximate double-precision floating point.\n- Use `/` for true division and `//` for floor division.\n- Use `math.isclose` instead of `==` for floating-point comparison; reach for `decimal.Decimal` when exact decimal precision is the domain requirement.\n:::\n', 'object-lifecycle.md': '+++\nslug = "object-lifecycle"\ntitle = "Object Lifecycle"\nsection = "Basics"\nsummary = "Names keep objects reachable until the last reference goes away."\ndoc_path = "/reference/datamodel.html#objects-values-and-types"\nsee_also = [\n "variables",\n "mutability",\n "classes",\n]\n+++\n\nPython objects live independently from the names that refer to them. Assignment adds another reference to an object; rebinding a name points that name somewhere else; `del` removes a name. The object can be reclaimed only after it is no longer reachable.\n\nMost programs do not manually destroy objects. They control lifetime by controlling which containers, local variables, and object attributes still hold references.\n\nThis example uses a small class so the object has visible state. The important evidence is that deleting one name does not destroy the object while another name still refers to it.\n\n:::program\n```python\nclass Box:\n def __init__(self, label):\n self.label = label\n\nbox = Box("draft")\nalias = box\n\nprint(box is alias)\nprint(alias.label)\n\nbox = Box("published")\nprint(alias.label)\nprint(box.label)\n\ndel alias\nprint("old object unreachable")\n```\n:::\n\n:::cell\nTwo names can refer to the same object. Mutating through one name would affect the object seen through the other.\n\n```python\nclass Box:\n def __init__(self, label):\n self.label = label\n\nbox = Box("draft")\nalias = box\n\nprint(box is alias)\nprint(alias.label)\n```\n\n```output\nTrue\ndraft\n```\n:::\n\n:::cell\nRebinding `box` does not change the original object. `alias` still reaches the first `Box` until that reference is removed too.\n\n```python\nbox = Box("published")\nprint(alias.label)\nprint(box.label)\n\ndel alias\nprint("old object unreachable")\n```\n\n```output\ndraft\npublished\nold object unreachable\n```\n:::\n\n:::note\n- Assignment binds names to objects; it does not copy the object.\n- `del name` removes one reference, not necessarily the object itself.\n- Python reclaims unreachable objects automatically, so lifetime bugs usually come from keeping references longer than intended.\n:::\n', 'operator-overloading.md': '+++\nslug = "operator-overloading"\ntitle = "Operator Overloading"\nsection = "Data Model"\nsummary = "Operator methods let objects define arithmetic and comparison syntax."\ndoc_path = "/reference/datamodel.html#emulating-numeric-types"\nsee_also = [\n "operators",\n "special-methods",\n "equality-and-identity",\n]\n+++\n\nOperator overloading lets a class define what expressions such as `a + b` mean for its objects. This is useful when the operation is part of the domain vocabulary.\n\nThe method should preserve the meaning readers expect from the operator. Vectors can add component by component; money can add amounts in the same currency; surprising overloads make code harder to trust.\n\nPython also has reflected methods such as `__radd__` for cases where the left operand does not know how to handle the right operand. That keeps mixed operations possible without making every type know every other type.\n\n:::program\n```python\nclass Vector:\n def __init__(self, x, y):\n self.x = x\n self.y = y\n\n def __add__(self, other):\n if not isinstance(other, Vector):\n return NotImplemented\n return Vector(self.x + other.x, self.y + other.y)\n\n def __eq__(self, other):\n if not isinstance(other, Vector):\n return NotImplemented\n return (self.x, self.y) == (other.x, other.y)\n\n def __repr__(self):\n return f"Vector({self.x}, {self.y})"\n\nprint(Vector(2, 3) + Vector(4, 5))\nprint(Vector(1, 1) == Vector(1, 1))\nprint(Vector(1, 1) == 5)\n```\n:::\n\n:::cell\n`__add__` defines how the `+` operator combines two objects. Checking the operand type and returning `NotImplemented` for foreign types lets Python try the other operand\'s reflected method instead of crashing inside yours.\n\n```python\nclass Vector:\n def __init__(self, x, y):\n self.x = x\n self.y = y\n\n def __add__(self, other):\n if not isinstance(other, Vector):\n return NotImplemented\n return Vector(self.x + other.x, self.y + other.y)\n\n def __repr__(self):\n return f"Vector({self.x}, {self.y})"\n\nprint(Vector(2, 3) + Vector(4, 5))\n```\n\n```output\nVector(6, 8)\n```\n:::\n\n:::cell\n`__eq__` defines value equality for `==`. Without it, user-defined objects compare by identity. Returning `NotImplemented` for foreign types matters most here: equality against an unrelated value should answer `False`, never raise — Python falls back to identity when both sides decline.\n\n```python\nclass Vector:\n def __init__(self, x, y):\n self.x = x\n self.y = y\n\n def __eq__(self, other):\n if not isinstance(other, Vector):\n return NotImplemented\n return (self.x, self.y) == (other.x, other.y)\n\nprint(Vector(1, 1) == Vector(1, 1))\nprint(Vector(1, 1) == 5)\n```\n\n```output\nTrue\nFalse\n```\n:::\n\n:::cell\nA useful `__repr__` makes operator results inspectable while debugging.\n\n```python\nclass Vector:\n def __init__(self, x, y):\n self.x = x\n self.y = y\n\n def __add__(self, other):\n if not isinstance(other, Vector):\n return NotImplemented\n return Vector(self.x + other.x, self.y + other.y)\n\n def __repr__(self):\n return f"Vector({self.x}, {self.y})"\n\nprint(repr(Vector(2, 3) + Vector(4, 5)))\n```\n\n```output\nVector(6, 8)\n```\n:::\n\n:::note\n- Overload operators only when the operation is unsurprising.\n- Return `NotImplemented` when an operand type is unsupported.\n- Implement equality deliberately when value comparison matters.\n:::\n', 'operators.md': '+++\nslug = "operators"\ntitle = "Operators"\nsection = "Basics"\nsummary = "Operators combine, compare, and test values in expressions."\ndoc_path = "/reference/expressions.html#operator-precedence"\nsee_also = [\n "numbers",\n "equality-and-identity",\n "assignment-expressions",\n "operator-overloading",\n]\n+++\n\nOperators are the punctuation and keywords that combine values into expressions. Some operators compute new values, some compare values, and some ask relationship questions such as membership or identity.\n\nThis page is the surface map. Focused examples explain the deeper behavior of numbers, booleans, conditions, sets, assignment expressions, and operator overloading.\n\nRead operators by the question they ask: arithmetic computes, comparison answers true or false, boolean operators combine truth values, membership searches a container, and specialized operators should only appear when the data shape calls for them.\n\n:::program\n```python\ncount = 10\nprint(count + 5)\nprint(count // 4)\nprint(count % 4)\nprint(2 ** 5)\n\nscore = 91\nprint(80 <= score < 100)\nprint(score == 100 or score >= 90)\nprint("py" in "python")\n\nflags = 0b0011\nprint(flags & 0b0101)\nprint(flags | 0b0100)\nprint(flags ^ 0b0101)\nprint(flags << 1)\n\nclass Scale:\n def __init__(self, value):\n self.value = value\n\n def __matmul__(self, other):\n return self.value * other.value\n\nprint(Scale(2) @ Scale(3))\n\nitems = ["a", "b"]\nif (size := len(items)) > 0:\n print(size)\n\ndef loud():\n print("ran")\n return True\n\nprint(False and loud())\nprint(True or loud())\nprint(True and loud())\n```\n:::\n\n:::cell\nArithmetic operators compute new values. Use `//` for floor division, `%` for remainder, and `**` for powers.\n\n```python\ncount = 10\nprint(count + 5)\nprint(count // 4)\nprint(count % 4)\nprint(2 ** 5)\n```\n\n```output\n15\n2\n2\n32\n```\n:::\n\n:::cell\nComparison operators produce booleans. Python comparisons can chain, which keeps range checks readable.\n\n```python\nscore = 91\nprint(80 <= score < 100)\nprint(score == 100 or score >= 90)\nprint("py" in "python")\n```\n\n```output\nTrue\nTrue\nTrue\n```\n:::\n\n:::cell\nBitwise operators work on integer bit patterns. They are useful for masks and flags, not ordinary boolean logic. `&` is bitwise AND, `|` is bitwise OR, `^` is exclusive OR, and `<<` shifts left.\n\n```python\nflags = 0b0011\nprint(flags & 0b0101)\nprint(flags | 0b0100)\nprint(flags ^ 0b0101)\nprint(flags << 1)\n```\n\n```output\n1\n7\n6\n6\n```\n:::\n\n:::cell\nThe `@` operator is reserved for matrix-like multiplication and custom types that define `__matmul__`.\n\n```python\nclass Scale:\n def __init__(self, value):\n self.value = value\n\n def __matmul__(self, other):\n return self.value * other.value\n\nprint(Scale(2) @ Scale(3))\n```\n\n```output\n6\n```\n:::\n\n:::cell\nThe walrus operator `:=` assigns inside an expression. Use it when naming a value avoids repeating work in a condition.\n\n```python\nitems = ["a", "b"]\nif (size := len(items)) > 0:\n print(size)\n```\n\n```output\n2\n```\n:::\n\n:::cell\n`and` and `or` short-circuit: the right side runs only when the left side cannot already determine the result. That makes them safe for guard expressions like `obj and obj.value` where the right side would fail on `None`.\n\n```python\ndef loud():\n print("ran")\n return True\n\nprint(False and loud())\nprint(True or loud())\nprint(True and loud())\n```\n\n```output\nFalse\nTrue\nran\nTrue\n```\n:::\n\n:::note\n- Use the clearest operator for the question: arithmetic, comparison, boolean logic, membership, identity, or bitwise manipulation.\n- `and` and `or` short-circuit, so the right side may not run.\n- Operators have precedence; use parentheses when grouping would otherwise be hard to read.\n- Custom operator behavior should make an object feel more natural, not more clever.\n:::\n', 'overloads.md': '+++\nslug = "overloads"\ntitle = "Overloads"\nsection = "Types"\nsummary = "overload describes APIs whose return type depends on argument types."\ndoc_path = "/library/typing.html#typing.overload"\nsee_also = [\n "type-hints",\n "union-and-optional-types",\n "generics-and-typevar",\n]\n+++\n\n`@overload` lets type checkers describe a function whose return type depends on the argument types. The overload declarations are static-only promises; the runtime function is still the single implementation that appears after them.\n\nUse overloads when a union return type would be too vague for callers. For example, `double(4)` returns an `int`, while `double("ha")` returns a `str`; `int | str` loses that relationship.\n\nAt runtime the overload stubs are not dispatch cases. The implementation must inspect or operate on the value just like any other Python function.\n\n:::program\n```python\nfrom typing import overload\n\n@overload\ndef double(value: int) -> int: ...\n\n@overload\ndef double(value: str) -> str: ...\n\ndef double(value: int | str) -> int | str:\n return value * 2\n\nprint(double(4))\nprint(double("ha"))\nprint(double.__annotations__)\n```\n:::\n\n:::cell\nThe overload stubs give static tools precise call shapes: integer in, integer out; string in, string out.\n\n```python\nfrom typing import overload\n\n@overload\ndef double(value: int) -> int: ...\n\n@overload\ndef double(value: str) -> str: ...\n\nprint("static signatures only")\n```\n\n```output\nstatic signatures only\n```\n:::\n\n:::cell\nThere is still one runtime implementation. It must accept every shape promised by the overloads.\n\n```python\ndef double(value: int | str) -> int | str:\n return value * 2\n\nprint(double(4))\nprint(double("ha"))\n```\n\n```output\n8\nhaha\n```\n:::\n\n:::cell\nOnly the implementation\'s annotations are visible on the runtime function. The overload declarations were for the type checker.\n\n```python\nprint(double.__annotations__)\n```\n\n```output\n{\'value\': int | str, \'return\': int | str}\n```\n:::\n\n:::note\n- Put `@overload` declarations immediately before the implementation.\n- Overloads improve static precision; they do not create runtime dispatch.\n- If all callers can work with one broad return type, a simple union annotation is usually enough.\n:::\n', 'packages.md': '+++\nslug = "packages"\ntitle = "Packages"\nsection = "Modules"\nsummary = "Packages organize modules into importable directories."\ndoc_path = "/tutorial/modules.html#packages"\nsee_also = [\n "modules",\n "import-aliases",\n "virtual-environments",\n]\n+++\n\nPackages are modules that can contain other modules. They let a project group related code behind dotted import paths such as `json.decoder` or `email.message`.\n\nAt runtime, importing a submodule gives Python a path through that package structure. In a project on disk, that structure is usually a directory with Python files and often an `__init__.py` file.\n\nUse packages when one module has grown into a small namespace of related modules. Keep module names boring and explicit so readers can tell where imported definitions come from.\n\n:::program\n```python\nimport importlib\nimport json\nimport json.decoder\n\nmodule = importlib.import_module("json.decoder")\n\nprint(json.__name__)\nprint(json.decoder.__name__)\nprint(module.JSONDecoder.__name__)\nprint(module is json.decoder)\n\n\nimport os\nimport sys\nimport tempfile\n\nwith tempfile.TemporaryDirectory() as tmp:\n pkg = os.path.join(tmp, "shapes")\n os.makedirs(pkg)\n with open(os.path.join(pkg, "__init__.py"), "w") as init:\n init.write("from .square import area\\n__all__ = [\'area\']\\n")\n with open(os.path.join(pkg, "square.py"), "w") as square:\n square.write("def area(side):\\n return side * side\\n")\n sys.path.insert(0, tmp)\n try:\n import shapes\n print(shapes.area(3))\n print(shapes.__all__)\n finally:\n sys.path.remove(tmp)\n sys.modules.pop("shapes", None)\n sys.modules.pop("shapes.square", None)\n```\n:::\n\n:::cell\nA package is itself a module. The `json` package exposes a namespace that can contain submodules.\n\n```python\nimport json\n\nprint(json.__name__)\n```\n\n```output\njson\n```\n:::\n\n:::cell\nDotted imports name a path through a package. Importing `json.decoder` makes that submodule available under the package namespace.\n\n```python\nimport json.decoder\n\nprint(json.decoder.__name__)\nprint(json.decoder.JSONDecoder.__name__)\n```\n\n```output\njson.decoder\nJSONDecoder\n```\n:::\n\n:::cell\n`importlib.import_module()` imports by string. It is useful for plugin systems and dynamic imports, but ordinary `import` is clearer when the dependency is known.\n\n```python\nimport importlib\n\nmodule = importlib.import_module("json.decoder")\nprint(module is json.decoder)\n```\n\n```output\nTrue\n```\n:::\n\n:::cell\nInside a package\'s `__init__.py`, `from .submodule import name` re-exports a submodule\'s name at the package root, and `__all__` lists the names that `from package import *` should make visible. This cell builds a temporary `shapes` package on disk to make both forms concrete.\n\n```python\nimport os\nimport sys\nimport tempfile\n\nwith tempfile.TemporaryDirectory() as tmp:\n pkg = os.path.join(tmp, "shapes")\n os.makedirs(pkg)\n with open(os.path.join(pkg, "__init__.py"), "w") as init:\n init.write("from .square import area\\n__all__ = [\'area\']\\n")\n with open(os.path.join(pkg, "square.py"), "w") as square:\n square.write("def area(side):\\n return side * side\\n")\n sys.path.insert(0, tmp)\n try:\n import shapes\n print(shapes.area(3))\n print(shapes.__all__)\n finally:\n sys.path.remove(tmp)\n sys.modules.pop("shapes", None)\n sys.modules.pop("shapes.square", None)\n```\n\n```output\n9\n[\'area\']\n```\n:::\n\n:::note\n- A package is a module that can contain submodules.\n- Dotted imports should mirror a meaningful project structure.\n- Use `from .submodule import name` inside a package to re-export submodule names; set `__all__` to declare the public surface.\n- Prefer ordinary imports unless the module name is truly dynamic.\n:::\n', 'paramspec.md': '+++\nslug = "paramspec"\ntitle = "ParamSpec"\nsection = "Types"\nsummary = "ParamSpec preserves callable parameter types through wrappers."\ndoc_path = "/library/typing.html#typing.ParamSpec"\nsee_also = [\n "callable-types",\n "decorators",\n "generics-and-typevar",\n]\n+++\n\n`ParamSpec` is for decorators and wrapper functions that should keep the wrapped callable\'s parameter shape. Without it, a generic decorator often falls back to `Callable[..., R]`, which says “this returns the right type, but I no longer know what arguments are valid.”\n\nUse `ParamSpec` when the wrapper forwards `*args` and `**kwargs` to the original function without changing the signature. Use a plain `Callable` when the wrapper deliberately accepts a different set of parameters.\n\n`P.args` and `P.kwargs` annotate the wrapper\'s forwarded arguments. A separate `TypeVar` keeps the return type tied to the wrapped function\'s return type.\n\n:::program\n```python\nfrom collections.abc import Callable\nfrom typing import ParamSpec, TypeVar\n\nP = ParamSpec("P")\nR = TypeVar("R")\n\n\ndef erased(func: Callable[..., R]) -> Callable[..., R]:\n return func\n\n\ndef logged(func: Callable[P, R]) -> Callable[P, R]:\n def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:\n print("calling", func.__name__)\n return func(*args, **kwargs)\n return wrapper\n\n@logged\ndef add(left: int, right: int) -> int:\n return left + right\n\nprint(erased(add)(2, 3))\nprint(add(2, 3))\n```\n:::\n\n:::cell\n`Callable[..., R]` is sometimes too broad. It preserves the return type, but the ellipsis means the callable accepts any argument list as far as the type checker can tell.\n\n```python\nfrom collections.abc import Callable\nfrom typing import ParamSpec, TypeVar\n\nR = TypeVar("R")\n\n\ndef erased(func: Callable[..., R]) -> Callable[..., R]:\n return func\n\nprint(erased.__name__)\n```\n\n```output\nerased\n```\n:::\n\n:::cell\n`ParamSpec` captures the original parameters and lets the wrapper forward exactly that shape.\n\n```python\nP = ParamSpec("P")\n\n\ndef logged(func: Callable[P, R]) -> Callable[P, R]:\n def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:\n print("calling", func.__name__)\n return func(*args, **kwargs)\n return wrapper\n\nprint(logged.__name__)\n```\n\n```output\nlogged\n```\n:::\n\n:::cell\nThe decorated function still runs normally. The benefit is static: tools can keep checking that `add` receives two integers.\n\n```python\n@logged\ndef add(left: int, right: int) -> int:\n return left + right\n\nprint(erased(add)(2, 3))\nprint(add(2, 3))\n```\n\n```output\ncalling add\n5\ncalling add\n5\n```\n:::\n\n:::note\n- `ParamSpec` preserves a callable\'s parameter list through transparent wrappers.\n- Pair `ParamSpec` with a `TypeVar` when the return type should also be preserved.\n- Python 3.12+ also accepts the inline PEP 695 spelling `def wrap[**P, R](func: Callable[P, R])`, which declares both variables in the signature itself.\n- If the wrapper changes the public signature, write that new signature directly instead.\n:::\n', 'partial-functions.md': '+++\nslug = "partial-functions"\ntitle = "Partial Functions"\nsection = "Functions"\nsummary = "functools.partial pre-fills arguments to make a more specific callable."\ndoc_path = "/library/functools.html#functools.partial"\nsee_also = [\n "functions",\n "args-and-kwargs",\n "callable-objects",\n]\n+++\n\n`functools.partial` turns a general callable into a more specific callable by remembering some positional or keyword arguments. It is useful when another API wants a one-argument callback but your underlying function needs more context.\n\nA partial object is still callable. It keeps the original function in `.func`, pre-filled positional arguments in `.args`, and pre-filled keyword arguments in `.keywords`.\n\nPrefer a named wrapper function when the adapted behavior needs branching, validation, or a docstring. Use `partial` when the adaptation is simply "call this function with these arguments already supplied."\n\n:::program\n```python\nfrom functools import partial\n\n\ndef apply_tax(rate, amount):\n return round(amount * (1 + rate), 2)\n\nvat = partial(apply_tax, 0.2)\nservice_tax = partial(apply_tax, rate=0.1)\n\nprint(apply_tax(0.2, 50))\nprint(vat(50))\nprint(service_tax(amount=80))\nprint(vat.func.__name__)\nprint(vat.args)\n```\n:::\n\n:::cell\nWithout `partial`, callers repeat the same fixed argument every time they want the specialized behavior.\n\n```python\ndef apply_tax(rate, amount):\n return round(amount * (1 + rate), 2)\n\nprint(apply_tax(0.2, 50))\n```\n\n```output\n60.0\n```\n:::\n\n:::cell\n`partial` stores that fixed argument and returns a callable shaped for the remaining arguments.\n\n```python\nfrom functools import partial\n\nvat = partial(apply_tax, 0.2)\nservice_tax = partial(apply_tax, rate=0.1)\n\nprint(vat(50))\nprint(service_tax(amount=80))\n```\n\n```output\n60.0\n88.0\n```\n:::\n\n:::cell\nPartial objects expose the function and stored arguments, which is helpful when debugging callback wiring.\n\n```python\nprint(vat.func.__name__)\nprint(vat.args)\n```\n\n```output\napply_tax\n(0.2,)\n```\n:::\n\n:::note\n- `partial` adapts a callable by pre-filling arguments.\n- The resulting object can be passed anywhere a callable with the remaining parameters is expected.\n- Use a regular function when the adapter needs more logic than argument binding.\n:::\n', 'positional-only-parameters.md': '+++\nslug = "positional-only-parameters"\ntitle = "Positional-only Parameters"\nsection = "Functions"\nsummary = "Use / to mark parameters that callers must pass by position."\ndoc_path = "/tutorial/controlflow.html#special-parameters"\nsee_also = [\n "keyword-only-arguments",\n "functions",\n "args-and-kwargs",\n]\n+++\n\nA `/` in a function signature marks the parameters before it as positional-only. Callers must pass those arguments by position, not by keyword.\n\nThis is useful when parameter names are implementation details or when an API should match built-in functions that accept positional values.\n\nTogether, `/` and `*` let a signature draw clear boundaries: positional-only inputs, ordinary inputs, and keyword-only options.\n\n:::program\n```python\ndef scale(value, /, factor=2, *, clamp=False):\n result = value * factor\n if clamp:\n result = min(result, 10)\n return result\n\nprint(scale(4))\nprint(scale(4, factor=3))\nprint(scale(4, factor=3, clamp=True))\n\ntry:\n scale(value=4)\nexcept TypeError as error:\n print(type(error).__name__)\n```\n:::\n\n:::cell\nParameters before `/` are positional-only. `value` is the main input, while `factor` remains an ordinary parameter that can be named.\n\n```python\ndef scale(value, /, factor=2, *, clamp=False):\n result = value * factor\n if clamp:\n result = min(result, 10)\n return result\n\nprint(scale(4))\nprint(scale(4, factor=3))\n```\n\n```output\n8\n12\n```\n:::\n\n:::cell\nParameters after `*` are keyword-only. That makes options such as `clamp` explicit at the call site — here `4 * 3` would be `12`, and the clamp visibly caps the result at `10`.\n\n```python\nprint(scale(4, factor=3, clamp=True))\n```\n\n```output\n10\n```\n:::\n\n:::cell\nThe restriction is enforced, not advisory: passing the positional-only `value` by keyword raises `TypeError` at the call site.\n\n```python\ntry:\n scale(value=4)\nexcept TypeError as error:\n print(type(error).__name__)\n```\n\n```output\nTypeError\n```\n:::\n\n:::note\n- `/` marks parameters before it as positional-only.\n- `*` marks parameters after it as keyword-only.\n- Use these markers when the call shape is part of the API design.\n:::\n', 'properties.md': '+++\nslug = "properties"\ntitle = "Properties"\nsection = "Classes"\nsummary = "@property keeps attribute syntax while adding computation or validation."\ndoc_path = "/library/functions.html#property"\nsee_also = [\n "classes",\n "attribute-access",\n "descriptors",\n "dataclasses",\n]\n+++\n\nProperties let a class keep a simple attribute-style API while running code behind the scenes. Callers write `box.area`, but the class can compute the value from current state.\n\nA property setter can validate assignment without changing the public spelling of the attribute. This is the boundary: plain attributes are enough for plain data, while properties are for computed or protected data.\n\nUse properties for cheap, attribute-like operations. Expensive work or actions with side effects should usually remain explicit methods.\n\n:::program\n```python\nclass Rectangle:\n def __init__(self, width, height):\n self.width = width\n self.height = height\n\n @property\n def area(self):\n return self.width * self.height\n\n @property\n def width(self):\n return self._width\n\n @width.setter\n def width(self, value):\n if value <= 0:\n raise ValueError("width must be positive")\n self._width = value\n\nbox = Rectangle(3, 4)\nprint(box.area)\n\nbox.width = 5\nprint(box.area)\n\ntry:\n box.width = 0\nexcept ValueError as error:\n print(error)\n```\n:::\n\n:::cell\nA read-only property exposes computed data through attribute access. `area` stays current because it is calculated from `width` and `height` each time it is read.\n\n```python\nclass Rectangle:\n def __init__(self, width, height):\n self.width = width\n self.height = height\n\n @property\n def area(self):\n return self.width * self.height\n\n @property\n def width(self):\n return self._width\n\n @width.setter\n def width(self, value):\n if value <= 0:\n raise ValueError("width must be positive")\n self._width = value\n\nbox = Rectangle(3, 4)\nprint(box.area)\n```\n\n```output\n12\n```\n:::\n\n:::cell\nA setter lets assignment keep normal attribute syntax while the class validates or normalizes the value.\n\n```python\nbox.width = 5\nprint(box.area)\n```\n\n```output\n20\n```\n:::\n\n:::cell\nValidation belongs inside the class when every caller should obey the same rule. Invalid assignment raises an exception at the boundary.\n\n```python\ntry:\n box.width = 0\nexcept ValueError as error:\n print(error)\n```\n\n```output\nwidth must be positive\n```\n:::\n\n:::note\n- Properties let APIs start simple and grow validation or computation later.\n- Callers access a property like an attribute, not like a method.\n- Use methods instead when work is expensive or action-like.\n:::\n', 'protocols.md': '+++\nslug = "protocols"\ntitle = "Protocols"\nsection = "Types"\nsummary = "Protocol describes required behavior for structural typing."\ndoc_path = "/library/typing.html#typing.Protocol"\nsee_also = [\n "type-hints",\n "classes",\n "inheritance-and-super",\n "abstract-base-classes",\n]\n+++\n\n`Protocol` describes the methods or attributes an object must provide. It exists for structural typing: if an object has the right shape, type checkers can treat it as compatible.\n\nThis is different from inheritance. Inheritance says a class is explicitly derived from a parent; a protocol says callers only need a particular behavior.\n\nAt runtime, ordinary method lookup still applies. Protocols are mainly for static analysis, documentation, and API boundaries.\n\n:::program\n```python\nfrom typing import Protocol\n\nclass Greeter(Protocol):\n def greet(self) -> str:\n ...\n\nclass Person:\n def __init__(self, name):\n self.name = name\n\n def greet(self):\n return f"hello {self.name}"\n\n\ndef welcome(greeter: Greeter):\n print(greeter.greet())\n\nwelcome(Person("Ada"))\nprint(Greeter.__name__)\n```\n:::\n\n:::cell\nA protocol names required behavior. The ellipsis marks the method body as intentionally unspecified, similar to an interface declaration.\n\n```python\nfrom typing import Protocol\n\nclass Greeter(Protocol):\n def greet(self) -> str:\n ...\n\nprint(Greeter.__name__)\n```\n\n```output\nGreeter\n```\n:::\n\n:::cell\nA class can satisfy the protocol without inheriting from it. `Person` has a compatible `greet()` method, so it has the right shape for static type checkers.\n\n```python\nclass Person:\n def __init__(self, name):\n self.name = name\n\n def greet(self):\n return f"hello {self.name}"\n\nprint(Person("Ada").greet())\n```\n\n```output\nhello Ada\n```\n:::\n\n:::cell\nUse the protocol as an annotation at the API boundary. The function only cares that the object can greet; it does not care about the concrete class.\n\n```python\ndef welcome(greeter: Greeter):\n print(greeter.greet())\n\nwelcome(Person("Ada"))\n```\n\n```output\nhello Ada\n```\n:::\n\n:::note\n- Protocols are for structural typing: compatibility by shape rather than explicit inheritance.\n- Type checkers understand protocols; normal runtime method calls still do the work.\n- Prefer inheritance when shared implementation matters, and protocols when only required behavior matters.\n:::\n', 'recursion.md': '+++\nslug = "recursion"\ntitle = "Recursion"\nsection = "Functions"\nsummary = "Recursive functions solve nested problems by calling themselves on smaller pieces."\ndoc_path = "/tutorial/controlflow.html#defining-functions"\nsee_also = [\n "functions",\n "conditionals",\n "generators",\n]\n+++\n\nA recursive function calls itself to solve a smaller piece of the same problem. Recursion exists for data that is naturally nested: trees, menus, expression nodes, and directory-like structures.\n\nEvery recursive function needs a base case that can be answered directly. The recursive case must move toward that base case by passing a smaller part of the data.\n\nPrefer loops for simple repetition over a flat sequence. Prefer recursion when the data shape is recursive too.\n\n:::program\n```python\ntree = {\n "value": 1,\n "children": [\n {"value": 2, "children": []},\n {"value": 3, "children": [{"value": 4, "children": []}]},\n ],\n}\n\ndef total(node):\n subtotal = node["value"]\n for child in node["children"]:\n subtotal += total(child)\n return subtotal\n\nprint(total({"value": 2, "children": []}))\nprint(total(tree))\n```\n:::\n\n:::cell\nA leaf node is the base case. It has no children, so the function can return its own value without making another recursive call.\n\n```python\ndef total(node):\n subtotal = node["value"]\n for child in node["children"]:\n subtotal += total(child)\n return subtotal\n\nprint(total({"value": 2, "children": []}))\n```\n\n```output\n2\n```\n:::\n\n:::cell\nA non-leaf node solves the same problem for each child, then combines those smaller totals with its own value.\n\n```python\ntree = {\n "value": 1,\n "children": [\n {"value": 2, "children": []},\n {"value": 3, "children": [{"value": 4, "children": []}]},\n ],\n}\n\nprint(total(tree))\n```\n\n```output\n10\n```\n:::\n\n:::note\n- Every recursive function needs a base case that stops the calls.\n- Recursion fits nested data better than flat repetition.\n- Python limits recursion depth, so loops are often better for very deep or simple repetition.\n:::\n', 'regular-expressions.md': '+++\nslug = "regular-expressions"\ntitle = "Regular Expressions"\nsection = "Text"\nsummary = "The re module searches and extracts text using regular expressions."\ndoc_path = "/library/re.html"\nsee_also = [\n "strings",\n "string-formatting",\n]\n+++\n\nRegular expressions are a compact language for searching and extracting text patterns. Python\'s `re` module provides the standard interface: `re.match` anchors at the start of the string, `re.search` finds the first occurrence anywhere, `re.findall` collects every match, `re.sub` rewrites matches, and `re.compile` reuses a pattern.\n\nUse regex when the pattern has structure: repeated records, alternatives, optional parts, or pieces you want to capture. Prefer ordinary string methods for simple substring checks because simpler code is easier to maintain.\n\nFlags such as `re.IGNORECASE` adjust matching behavior without rewriting the pattern. Pair them with `re.compile` when the same pattern is used repeatedly.\n\n:::program\n```python\nimport re\n\ntext = "Ada: 10, Grace: 9"\npattern = r"([A-Za-z]+): (\\d+)"\n\nfor name, score in re.findall(pattern, text):\n print(name, int(score))\n\nmatch = re.search(r"Grace: (\\d+)", text)\nprint(match.group(1))\nprint("Grace" in text)\n\nstart = re.match(r"Ada", text)\nprint(start is not None)\nprint(re.match(r"Grace", text))\n\nscoreline = re.compile(pattern)\nprint(scoreline.findall(text))\n\ncasey = "ADA: 11"\nprint(re.search(r"ada", casey, flags=re.IGNORECASE).group(0))\n\nprint(re.sub(r"\\d+", "?", text))\n```\n:::\n\n:::cell\nRaw strings keep backslashes readable in regex patterns. Capturing groups return just the pieces inside parentheses.\n\n```python\nimport re\n\ntext = "Ada: 10, Grace: 9"\npattern = r"([A-Za-z]+): (\\d+)"\n\nfor name, score in re.findall(pattern, text):\n print(name, int(score))\n```\n\n```output\nAda 10\nGrace 9\n```\n:::\n\n:::cell\n`re.search()` finds the first match. A match object exposes captured groups by position.\n\n```python\nmatch = re.search(r"Grace: (\\d+)", text)\nprint(match.group(1))\n```\n\n```output\n9\n```\n:::\n\n:::cell\nFor a simple substring check, ordinary string membership is clearer than regex.\n\n```python\nprint("Grace" in text)\n```\n\n```output\nTrue\n```\n:::\n\n:::cell\n`re.match` only matches at the start of the string; `re.search` finds the first match anywhere. Picking the right one keeps anchoring intent visible without an explicit `^`.\n\n```python\nstart = re.match(r"Ada", text)\nprint(start is not None)\nprint(re.match(r"Grace", text))\n```\n\n```output\nTrue\nNone\n```\n:::\n\n:::cell\n`re.compile` produces a reusable pattern object and gives the pattern a name. The `re` module also caches recently compiled patterns internally, so the practical wins are readability and a place to attach flags more than raw speed.\n\n```python\nscoreline = re.compile(pattern)\nprint(scoreline.findall(text))\n```\n\n```output\n[(\'Ada\', \'10\'), (\'Grace\', \'9\')]\n```\n:::\n\n:::cell\nFlags such as `re.IGNORECASE` adjust matching without changing the pattern. `re.sub` replaces every match with a replacement string and returns the rewritten text.\n\n```python\ncasey = "ADA: 11"\nprint(re.search(r"ada", casey, flags=re.IGNORECASE).group(0))\n\nprint(re.sub(r"\\d+", "?", text))\n```\n\n```output\nADA\nAda: ?, Grace: ?\n```\n:::\n\n:::note\n- Use raw strings for regex patterns so backslashes are easier to read.\n- Use capturing groups when the point is extraction, not just matching.\n- `re.match` anchors at the start; `re.search` finds the first match anywhere.\n- `re.compile` saves work when the pattern runs more than once.\n- `re.sub` rewrites matches; flags like `re.IGNORECASE` change matching behavior without rewriting the pattern.\n- Reach for string methods before regex when the pattern is simple.\n:::\n', 'runtime-type-checks.md': '+++\nslug = "runtime-type-checks"\ntitle = "Runtime Type Checks"\nsection = "Types"\nsummary = "type, isinstance, and issubclass inspect runtime relationships."\ndoc_path = "/library/functions.html#isinstance"\nsee_also = [\n "type-hints",\n "protocols",\n "casts-and-any",\n "abstract-base-classes",\n]\n+++\n\nRuntime type checks inspect real objects while the program is running. They are different from type hints, which mostly guide tools before the program runs.\n\nUse `type()` when the exact class matters, `isinstance()` when subclasses should count, and `issubclass()` when checking class relationships. Most APIs prefer behavior over type checks, but runtime checks are useful at input boundaries.\n\nDo not turn every function into a wall of `isinstance()` calls. If the code only needs an object that can perform an operation, duck typing or a protocol may be clearer.\n\n:::program\n```python\nclass Animal:\n pass\n\nclass Dog(Animal):\n pass\n\npet = Dog()\n\nprint(type(pet).__name__)\nprint(type(pet) is Animal)\nprint(isinstance(pet, Animal))\nprint(issubclass(Dog, Animal))\n```\n:::\n\n:::cell\n`type()` reports the exact runtime class. A `Dog` instance is not exactly an `Animal` instance.\n\n```python\nclass Animal:\n pass\n\nclass Dog(Animal):\n pass\n\npet = Dog()\nprint(type(pet).__name__)\nprint(type(pet) is Animal)\n```\n\n```output\nDog\nFalse\n```\n:::\n\n:::cell\n`isinstance()` accepts subclasses, which is usually what API boundaries want.\n\n```python\nprint(isinstance(pet, Dog))\nprint(isinstance(pet, Animal))\n```\n\n```output\nTrue\nTrue\n```\n:::\n\n:::cell\n`issubclass()` checks class relationships rather than individual objects.\n\n```python\nprint(issubclass(Dog, Animal))\n```\n\n```output\nTrue\n```\n:::\n\n:::note\n- `type()` is exact; `isinstance()` follows inheritance.\n- Runtime checks inspect objects, not static annotations.\n- Prefer behavior, protocols, or clear validation over scattered type checks.\n:::\n', 'scope-global-nonlocal.md': '+++\nslug = "scope-global-nonlocal"\ntitle = "Global and Nonlocal"\nsection = "Functions"\nsummary = "global and nonlocal choose which outer binding assignment should update."\ndoc_path = "/reference/simple_stmts.html#the-global-statement"\nsee_also = [\n "variables",\n "closures",\n "functions",\n]\n+++\n\nAssignment normally creates or updates a local name inside the current function. `global` and `nonlocal` are explicit escape hatches for rebinding names outside that local scope.\n\nUse `nonlocal` when an inner function should update a name in an enclosing function. Use `global` rarely; passing values and returning results is usually clearer.\n\nThese statements affect name binding, not object mutation. Mutating a shared list is different from rebinding the name itself.\n\n:::program\n```python\ncount = 0\n\ndef bump_global():\n global count\n count += 1\n\nbump_global()\nprint(count)\n\n\ndef make_counter():\n total = 0\n def bump():\n nonlocal total\n total += 1\n return total\n return bump\n\ncounter = make_counter()\nprint(counter())\nprint(counter())\n```\n:::\n\n:::cell\n`global` tells assignment to update a module-level binding. Without it, `count += 1` would try to assign a local `count`.\n\n```python\ncount = 0\n\ndef bump_global():\n global count\n count += 1\n\nbump_global()\nprint(count)\n```\n\n```output\n1\n```\n:::\n\n:::cell\n`nonlocal` tells assignment to update a binding in the nearest enclosing function scope. This is useful for small closures that keep state.\n\n```python\ndef make_counter():\n total = 0\n def bump():\n nonlocal total\n total += 1\n return total\n return bump\n\ncounter = make_counter()\nprint(counter())\nprint(counter())\n```\n\n```output\n1\n2\n```\n:::\n\n:::note\n- Assignment inside a function is local unless declared otherwise.\n- Prefer `nonlocal` for closure state and avoid `global` unless module state is truly intended.\n- Passing values and returning results is usually easier to test than rebinding outer names.\n:::\n', 'sentinel-iteration.md': '+++\nslug = "sentinel-iteration"\ntitle = "Sentinel Iteration"\nsection = "Iteration"\nsummary = "iter(callable, sentinel) repeats calls until a marker value appears."\ndoc_path = "/library/functions.html#iter"\nsee_also = [\n "iterators",\n "while-loops",\n "break-and-continue",\n]\n+++\n\n`iter(callable, sentinel)` calls a zero-argument callable over and over. It yields each result until the callable returns the sentinel value, and the sentinel itself is not yielded.\n\nThis shape is useful for repeated reads: file blocks until `b""`, socket chunks until an empty response, queue items until a stop marker. It removes the common `while True` plus `break` scaffolding when the loop body is otherwise just "read, then process".\n\nThe callable must take no arguments. Wrap a parameterized reader in a `lambda`, `functools.partial`, or object method when the underlying API needs parameters.\n\n:::program\n```python\nchunks = iter(["py", "thon", ""])\n\n\ndef read_chunk():\n return next(chunks)\n\nprint(list(iter(read_chunk, "")))\n\nchunks = iter(["py", "thon", ""])\nword = ""\nwhile True:\n chunk = next(chunks)\n if chunk == "":\n break\n word += chunk\nprint(word)\n```\n:::\n\n:::cell\nThe two-argument form turns a polling callable into an iterator. The empty string stops the loop without appearing in the result.\n\n```python\nchunks = iter(["py", "thon", ""])\n\n\ndef read_chunk():\n return next(chunks)\n\nprint(list(iter(read_chunk, "")))\n```\n\n```output\n[\'py\', \'thon\']\n```\n:::\n\n:::cell\nThe equivalent manual loop needs an explicit read, comparison, and `break`. Use this shape when the stop condition is more complicated than a single sentinel value.\n\n```python\nchunks = iter(["py", "thon", ""])\nword = ""\nwhile True:\n chunk = next(chunks)\n if chunk == "":\n break\n word += chunk\nprint(word)\n```\n\n```output\npython\n```\n:::\n\n:::note\n- The callable passed to `iter(callable, sentinel)` must take no arguments.\n- The sentinel stops iteration and is not yielded.\n- When the loop needs richer branching, an explicit `while` loop may be clearer.\n:::\n', 'sets.md': '+++\nslug = "sets"\ntitle = "Sets"\nsection = "Collections"\nsummary = "Sets store unique values and make membership checks explicit."\ndoc_path = "/tutorial/datastructures.html#sets"\nsee_also = [\n "lists",\n "dicts",\n "comprehensions",\n]\n+++\n\nSets store unique hashable values. Use them when membership and de-duplication matter more than order.\n\nA list can answer membership with `in`, but a set communicates that membership is the main operation. Set algebra then expresses how groups relate to each other.\n\nBecause sets are unordered, examples often wrap output in `sorted()` so the display is deterministic.\n\n:::program\n```python\nlanguages = ["python", "go", "python"]\nunique_languages = set(languages)\nprint(sorted(unique_languages))\n\nallowed = {"python", "rust"}\nprint("python" in allowed)\nprint("ruby" in allowed)\n\ncompiled = {"go", "rust"}\nprint(sorted(allowed | compiled))\nprint(sorted(allowed & compiled))\nprint(sorted(allowed - compiled))\n```\n:::\n\n:::cell\nCreating a set removes duplicates. Keep a list when order and repeated values matter; convert to a set when uniqueness is the point.\n\n```python\nlanguages = ["python", "go", "python"]\nunique_languages = set(languages)\nprint(sorted(unique_languages))\n```\n\n```output\n[\'go\', \'python\']\n```\n:::\n\n:::cell\nMembership checks are the everyday set operation. A list can also use `in`, but a set says that membership is central to the data shape.\n\n```python\nallowed = {"python", "rust"}\nprint("python" in allowed)\nprint("ruby" in allowed)\n```\n\n```output\nTrue\nFalse\n```\n:::\n\n:::cell\nUnion, intersection, and difference describe relationships between groups without manual loops.\n\n```python\ncompiled = {"go", "rust"}\nprint(sorted(allowed | compiled))\nprint(sorted(allowed & compiled))\nprint(sorted(allowed - compiled))\n```\n\n```output\n[\'go\', \'python\', \'rust\']\n[\'rust\']\n[\'python\']\n```\n:::\n\n:::note\n- Use sets when uniqueness and membership are the main operations.\n- Prefer lists when order or repeated values are part of the meaning.\n- Sets are unordered, so sort them when examples need deterministic display.\n:::\n', 'slices.md': '+++\nslug = "slices"\ntitle = "Slices"\nsection = "Collections"\nsummary = "Slices copy meaningful ranges from ordered sequences."\ndoc_path = "/tutorial/introduction.html#lists"\nsee_also = [\n "lists",\n "tuples",\n "strings",\n]\n+++\n\nSlicing reads a range from an ordered sequence with `start:stop:step`. It exists because Python code often needs a meaningful piece of a sequence: a page, a prefix, a tail, a stride, or a reversed view.\n\nThe stop index is excluded. That convention makes lengths and adjacent ranges line up: `items[:3]` and `items[3:]` split a sequence without overlap.\n\nSlices return new sequence objects for built-in lists and strings. Use indexing for one item; use slicing when the result should still be a sequence.\n\n:::program\n```python\nletters = ["a", "b", "c", "d", "e", "f"]\nfirst_page = letters[:3]\nrest = letters[3:]\nprint(first_page)\nprint(rest)\n\nmiddle = letters[1:5]\nevery_other = letters[::2]\nreversed_letters = letters[::-1]\nprint(middle)\nprint(every_other)\nprint(reversed_letters)\nprint(letters)\n```\n:::\n\n:::cell\nOmitted bounds mean “from the beginning” or “through the end.” Because the stop index is excluded, adjacent slices split a sequence cleanly.\n\n```python\nletters = ["a", "b", "c", "d", "e", "f"]\nfirst_page = letters[:3]\nrest = letters[3:]\nprint(first_page)\nprint(rest)\n```\n\n```output\n[\'a\', \'b\', \'c\']\n[\'d\', \'e\', \'f\']\n```\n:::\n\n:::cell\nUse `start:stop` for a middle range and `step` when you want to skip or walk backward. These operations return new lists; the original list is unchanged.\n\n```python\nmiddle = letters[1:5]\nevery_other = letters[::2]\nreversed_letters = letters[::-1]\nprint(middle)\nprint(every_other)\nprint(reversed_letters)\nprint(letters)\n```\n\n```output\n[\'b\', \'c\', \'d\', \'e\']\n[\'a\', \'c\', \'e\']\n[\'f\', \'e\', \'d\', \'c\', \'b\', \'a\']\n[\'a\', \'b\', \'c\', \'d\', \'e\', \'f\']\n```\n:::\n\n:::note\n- Slice stop indexes are excluded, so adjacent ranges compose cleanly.\n- Omitted bounds mean the beginning or end of the sequence.\n- A negative step walks backward; `[::-1]` is a common reversed-copy idiom.\n:::\n', 'sorting.md': '+++\nslug = "sorting"\ntitle = "Sorting"\nsection = "Collections"\nsummary = "sorted returns a new ordered list and key functions choose the sort value."\ndoc_path = "/howto/sorting.html"\nsee_also = [\n "lists",\n "lambdas",\n "functions",\n]\n+++\n\n`sorted()` accepts any iterable and returns a new list. The original collection is left untouched, which makes `sorted()` useful in expressions and pipelines.\n\nUse `key=` to say what value should be compared for each item. This is the idiomatic way to sort records, tuples, dictionaries, and objects by a field.\n\nUse `reverse=True` for descending order. Use `list.sort()` instead when you intentionally want to mutate an existing list in place.\n\n:::program\n```python\nnames = ["Guido", "Ada", "Grace"]\nprint(sorted(names))\nprint(names)\n\nusers = [\n {"name": "Ada", "score": 10},\n {"name": "Guido", "score": 8},\n {"name": "Grace", "score": 10},\n]\nranked = sorted(users, key=lambda user: user["score"], reverse=True)\nprint([user["name"] for user in ranked])\n\nusers.sort(key=lambda user: user["name"])\nprint([user["name"] for user in users])\n```\n:::\n\n:::cell\n`sorted()` returns a new list. Printing the original list afterward shows that the input order did not change.\n\n```python\nnames = ["Guido", "Ada", "Grace"]\nprint(sorted(names))\nprint(names)\n```\n\n```output\n[\'Ada\', \'Grace\', \'Guido\']\n[\'Guido\', \'Ada\', \'Grace\']\n```\n:::\n\n:::cell\nA key function computes the value to compare. Here the records are sorted by score, highest first, and the output shows the resulting order.\n\n```python\nusers = [\n {"name": "Ada", "score": 10},\n {"name": "Guido", "score": 8},\n {"name": "Grace", "score": 10},\n]\nranked = sorted(users, key=lambda user: user["score"], reverse=True)\nprint([user["name"] for user in ranked])\n```\n\n```output\n[\'Ada\', \'Grace\', \'Guido\']\n```\n:::\n\n:::cell\n`list.sort()` sorts the list in place. Use it when mutation is the point and no separate sorted copy is needed.\n\n```python\nusers.sort(key=lambda user: user["name"])\nprint([user["name"] for user in users])\n```\n\n```output\n[\'Ada\', \'Grace\', \'Guido\']\n```\n:::\n\n:::note\n- `sorted()` makes a new list; `list.sort()` mutates an existing list.\n- `key=` should return the value Python compares for each item.\n- Python\'s sort is stable, so equal keys keep their original relative order.\n:::\n', 'special-methods.md': '+++\nslug = "special-methods"\ntitle = "Special Methods"\nsection = "Data Model"\nsummary = "Special methods connect your objects to Python syntax and built-ins."\ndoc_path = "/reference/datamodel.html#special-method-names"\nsee_also = [\n "container-protocols",\n "operator-overloading",\n "callable-objects",\n "context-managers",\n]\n+++\n\nSpecial methods, often called dunder methods, connect user-defined classes to Python syntax and built-ins such as len(), iter(), and repr().\n\nImplementing these methods lets your objects participate in Python protocols rather than forcing callers to learn custom method names for common operations.\n\nGood special methods make objects feel boring in the best way: they work with the language features Python programmers already know.\n\n:::program\n```python\nclass Bag:\n def __init__(self, items):\n self.items = list(items)\n\n def __len__(self):\n return len(self.items)\n\n def __iter__(self):\n return iter(self.items)\n\n def __repr__(self):\n return f"Bag({self.items!r})"\n\n def __str__(self):\n return ", ".join(self.items)\n\n def __eq__(self, other):\n return isinstance(other, Bag) and self.items == other.items\n\n def __hash__(self):\n return hash(tuple(self.items))\n\n def __lt__(self, other):\n return self.items < other.items\n\n def __contains__(self, item):\n return item in self.items\n\n def __getitem__(self, index):\n return self.items[index]\n\n def __setitem__(self, index, value):\n self.items[index] = value\n\n def __bool__(self):\n return bool(self.items)\n\nbag = Bag(["a", "b"])\nprint(len(bag))\nprint(list(bag))\nprint(bag)\nprint(repr(bag))\nprint(Bag(["a", "b"]) == Bag(["a", "b"]))\nprint(Bag(["a"]) < Bag(["a", "b"]))\nprint(hash(Bag(["a"])) == hash(Bag(["a"])))\nprint("a" in bag)\nprint(bag[0])\nbag[1] = "z"\nprint(list(bag))\nprint(bool(Bag([])))\n\n\nclass Multiplier:\n def __init__(self, factor):\n self.factor = factor\n\n def __call__(self, value):\n return value * self.factor\n\ntriple = Multiplier(3)\nprint(triple(5))\n\n\nclass Trace:\n def __enter__(self):\n print("enter")\n return self\n\n def __exit__(self, *exc):\n print("exit")\n return False\n\nwith Trace():\n print("inside")\n```\n:::\n\n:::cell\nStart with a normal class that stores its data. Special methods build on ordinary instance state.\n\n```python\nclass Bag:\n def __init__(self, items):\n self.items = list(items)\n\nbag = Bag(["a", "b"])\nprint(bag.items)\n```\n\n```output\n[\'a\', \'b\']\n```\n:::\n\n:::cell\nImplement `__len__` to let `len()` ask the object for its size using Python\'s standard protocol.\n\n```python\nclass Bag:\n def __init__(self, items):\n self.items = list(items)\n\n def __len__(self):\n return len(self.items)\n\nbag = Bag(["a", "b"])\nprint(len(bag))\n```\n\n```output\n2\n```\n:::\n\n:::cell\nImplement `__iter__` to make the object iterable. Then tools such as `list()` can consume it without a custom method name.\n\n```python\nclass Bag:\n def __init__(self, items):\n self.items = list(items)\n\n def __len__(self):\n return len(self.items)\n\n def __iter__(self):\n return iter(self.items)\n\nbag = Bag(["a", "b"])\nprint(list(bag))\n```\n\n```output\n[\'a\', \'b\']\n```\n:::\n\n:::cell\nImplement `__repr__` to give the object a useful developer-facing representation when it is printed or inspected. With no `__str__` defined, `print()` falls back to `__repr__`.\n\n```python\nclass Bag:\n def __init__(self, items):\n self.items = list(items)\n\n def __len__(self):\n return len(self.items)\n\n def __iter__(self):\n return iter(self.items)\n\n def __repr__(self):\n return f"Bag({self.items!r})"\n\nbag = Bag(["a", "b"])\nprint(bag)\n```\n\n```output\nBag([\'a\', \'b\'])\n```\n:::\n\n:::cell\nAdd `__str__` for an end-user representation. `print()` and `str()` prefer `__str__`; `repr()` and the REPL still use `__repr__`. Keep `__repr__` unambiguous for debugging and let `__str__` be the friendly form.\n\n```python\nclass Bag:\n def __init__(self, items):\n self.items = list(items)\n\n def __repr__(self):\n return f"Bag({self.items!r})"\n\n def __str__(self):\n return ", ".join(self.items)\n\nbag = Bag(["a", "b"])\nprint(bag)\nprint(repr(bag))\n```\n\n```output\na, b\nBag([\'a\', \'b\'])\n```\n:::\n\n:::cell\n`__eq__` decides what equality means for the type, comparing contents. `__lt__` orders by those same contents, so ordering stays consistent with equality (define one comparison and `functools.total_ordering` can fill in the rest; `__lt__` alone is enough for `<` and `sorted()`). Defining `__eq__` removes the default `__hash__`, so add it back only for types you treat as immutable: this `Bag` hashes its current items, so the last two lines show the hazard — a `Bag` found in a set becomes unfindable once its items change, because its hash no longer points at the bucket it was stored in.\n\n```python\nclass Bag:\n def __init__(self, items):\n self.items = list(items)\n\n def __eq__(self, other):\n return isinstance(other, Bag) and self.items == other.items\n\n def __hash__(self):\n return hash(tuple(self.items))\n\n def __lt__(self, other):\n return self.items < other.items\n\nprint(Bag(["a", "b"]) == Bag(["a", "b"]))\nprint(Bag(["a"]) < Bag(["a", "b"]))\n\nbag = Bag(["a"])\nseen = {bag}\nprint(bag in seen)\nbag.items.append("b")\nprint(bag in seen)\n```\n\n```output\nTrue\nTrue\nTrue\nFalse\n```\n:::\n\n:::cell\nThe container protocols make instances behave like built-in containers. `__contains__` powers `in`, `__getitem__`/`__setitem__` power subscription, and `__bool__` decides truthiness for `if` and `while`. See [container-protocols](/examples/container-protocols) for the full surface.\n\n```python\nclass Bag:\n def __init__(self, items):\n self.items = list(items)\n\n def __contains__(self, item):\n return item in self.items\n\n def __getitem__(self, index):\n return self.items[index]\n\n def __setitem__(self, index, value):\n self.items[index] = value\n\n def __bool__(self):\n return bool(self.items)\n\nbag = Bag(["a", "b"])\nprint("a" in bag)\nprint(bag[0])\nbag[1] = "z"\nprint(bag.items)\nprint(bool(Bag([])))\n```\n\n```output\nTrue\na\n[\'a\', \'z\']\nFalse\n```\n:::\n\n:::cell\n`__call__` makes an instance callable like a function — useful for stateful operations whose configuration deserves a name. `__enter__` and `__exit__` make a class a context manager so it can be used with `with`. The focused [callable-objects](/examples/callable-objects) and [context-managers](/examples/context-managers) pages go deeper.\n\n```python\nclass Multiplier:\n def __init__(self, factor):\n self.factor = factor\n\n def __call__(self, value):\n return value * self.factor\n\ntriple = Multiplier(3)\nprint(triple(5))\n\n\nclass Trace:\n def __enter__(self):\n print("enter")\n return self\n\n def __exit__(self, *exc):\n print("exit")\n return False\n\nwith Trace():\n print("inside")\n```\n\n```output\n15\nenter\ninside\nexit\n```\n:::\n\n:::note\n- Dunder methods are looked up by Python\'s data model protocols.\n- `__repr__` is the developer-facing form; `__str__` is the user-facing form. `print()` falls back to `__repr__` when `__str__` is missing.\n- Defining `__eq__` removes the default `__hash__`; restore it when the type should be hashable.\n- Container protocols (`__contains__`, `__getitem__`, `__setitem__`, `__bool__`) make instances behave like built-in containers.\n- `__call__` makes instances callable; `__enter__`/`__exit__` make them context managers.\n- Implement the smallest protocol that makes your object feel native.\n:::\n', 'string-formatting.md': '+++\nslug = "string-formatting"\ntitle = "String Formatting"\nsection = "Text"\nsummary = "f-strings turn values into readable text at the point of use."\ndoc_path = "/tutorial/inputoutput.html#formatted-string-literals"\nsee_also = [\n "strings",\n "logging",\n "csv-data",\n "values",\n]\n+++\n\nFormatted string literals, or f-strings, exist because programs constantly need to turn values into human-readable text. They keep the expression next to the words it explains.\n\nFormat specifications after `:` control presentation details such as width, alignment, padding, and precision. This separates the value being computed from the way it should be displayed.\n\nUse f-strings for most new formatting code. They relate directly to expressions: anything inside braces is evaluated, then formatted into the surrounding string.\n\n:::program\n```python\nname = "Ada"\nscore = 9.5\nrank = 1\n\nmessage = f"{name} scored {score}"\nprint(message)\n\nrow = f"{rank:>2} | {name:<8} | {score:05.1f}"\nprint(row)\n\nprint(f"{score = }")\n```\n:::\n\n:::cell\nAn f-string evaluates expressions inside braces and inserts their string form into the surrounding text. This is clearer than joining several converted values by hand.\n\n```python\nname = "Ada"\nscore = 9.5\nrank = 1\n\nmessage = f"{name} scored {score}"\nprint(message)\n```\n\n```output\nAda scored 9.5\n```\n:::\n\n:::cell\nFormat specifications after `:` control display without changing the underlying values. Here the rank is right-aligned, the name is left-aligned, and `05.1f` zero-pads the score to a width of five characters with one decimal place.\n\n```python\nrow = f"{rank:>2} | {name:<8} | {score:05.1f}"\nprint(row)\n```\n\n```output\n 1 | Ada | 009.5\n```\n:::\n\n:::cell\nThe debug form with `=` is useful while learning or logging because it prints both the expression and the value.\n\n```python\nprint(f"{score = }")\n```\n\n```output\nscore = 9.5\n```\n:::\n\n:::note\n- Use `f"..."` strings for most new formatting code.\n- Expressions inside braces are evaluated before formatting.\n- Format specifications after `:` control alignment, width, padding, and precision.\n:::\n', 'strings.md': '+++\nslug = "strings"\ntitle = "Strings"\nsection = "Text"\nsummary = "Strings are immutable Unicode text sequences."\ndoc_path = "/library/stdtypes.html#text-sequence-type-str"\nsee_also = [\n "values",\n "string-formatting",\n "bytes-and-bytearray",\n "regular-expressions",\n]\n+++\n\nPython strings are immutable Unicode text sequences. A `str` stores text as Unicode code points, so it can represent English, Thai, accented letters, emoji, and ordinary ASCII with the same type.\n\nUnicode matters because text length and byte length are different questions. The English word `"hello"` uses five code points and five UTF-8 bytes because ASCII characters encode as one byte each. The Thai greeting `"สวัสดี"` has six code points but needs eighteen UTF-8 bytes.\n\nUse `str` when you mean text, and encode to `bytes` only at boundaries such as files, network protocols, and binary APIs. String operations such as `upper()` and `strip()` return new strings instead of changing the original.\n\n:::program\n```python\nenglish = "hello"\nfrench = "café"\nthai = "สวัสดี"\n\nfor label, word in [("English", english), ("French", french), ("Thai", thai)]:\n print(label, word, len(word), len(word.encode("utf-8")))\n\nprint(thai[0])\nprint([hex(ord(char)) for char in thai[:2]])\n\ntext = " café "\nclean = text.strip()\nprint(clean)\nprint(clean.upper())\nprint(clean.encode("utf-8"))\n```\n:::\n\n:::cell\nCompare three words by code-point count and UTF-8 byte count. ASCII characters take one byte each (`hello` → 5 bytes); the `é` in `café` is one code point but two UTF-8 bytes; each Thai character takes three. The `str` type abstracts over all three.\n\n```python\nenglish = "hello"\nfrench = "café"\nthai = "สวัสดี"\n\nfor label, word in [("English", english), ("French", french), ("Thai", thai)]:\n print(label, word, len(word), len(word.encode("utf-8")))\n```\n\n```output\nEnglish hello 5 5\nFrench café 4 5\nThai สวัสดี 6 18\n```\n:::\n\n:::cell\nIndexing and iteration work with Unicode code points, not encoded bytes. `ord()` returns the integer code point, which is often displayed in hexadecimal when teaching text encoding.\n\n```python\nprint(thai[0])\nprint([hex(ord(char)) for char in thai[:2]])\n```\n\n```output\nส\n[\'0xe2a\', \'0xe27\']\n```\n:::\n\n:::cell\nString methods return new strings because strings are immutable. Encoding turns text into bytes when another system needs a byte representation.\n\n```python\ntext = " café "\nclean = text.strip()\nprint(clean)\nprint(clean.upper())\nprint(clean.encode("utf-8"))\n```\n\n```output\ncafé\nCAFÉ\nb\'caf\\xc3\\xa9\'\n```\n:::\n\n:::note\n- Use `str` for text and `bytes` for binary data.\n- `len(text)` counts Unicode code points; `len(text.encode("utf-8"))` counts encoded bytes.\n- ASCII text is a useful baseline because each ASCII code point is one UTF-8 byte.\n- String methods return new strings because strings are immutable.\n- User-visible “characters” can be more subtle than code points; combining marks and emoji sequences may need specialized text handling.\n:::\n', 'structured-data-shapes.md': '+++\nslug = "structured-data-shapes"\ntitle = "Structured Data Shapes"\nsection = "Classes"\nsummary = "dataclass, NamedTuple, and TypedDict each model records with different trade-offs."\ndoc_path = "/library/dataclasses.html"\nsee_also = [\n "dataclasses",\n "typed-dicts",\n "tuples",\n "classes",\n]\n+++\n\n`@dataclass`, `typing.NamedTuple`, and `typing.TypedDict` are three ways to give a record a name and a schema. They model the same data but differ in mutability, access syntax, and what the type information costs at runtime.\n\nA dataclass is a regular class with `__init__` and `__repr__` generated for you, so instances are mutable and attribute-accessed. A `NamedTuple` is a tuple subclass with named positions, so instances are immutable and support both `obj.field` and `obj[index]`. A `TypedDict` is a plain dict at runtime; the schema lives only in the type checker.\n\nPick the shape that matches the problem: a dataclass when methods or mutability help; a `NamedTuple` for small immutable records that benefit from unpacking; a `TypedDict` for JSON-shaped data that should stay as a dict at the boundary.\n\n:::program\n```python\nfrom dataclasses import dataclass\nfrom typing import NamedTuple, TypedDict\n\n@dataclass\nclass UserClass:\n name: str\n score: int\n\nclass UserTuple(NamedTuple):\n name: str\n score: int\n\nclass UserDict(TypedDict):\n name: str\n score: int\n\na = UserClass("Ada", 98)\nprint(a)\na.score = 100\nprint(a.score)\n\nb = UserTuple("Ada", 98)\nprint(b)\nprint(b.name, b[1])\nprint(b._replace(score=100))\n\nc: UserDict = {"name": "Ada", "score": 98}\nprint(c)\nprint(c["name"])\nprint(type(c).__name__)\n\nprint(isinstance(a, UserClass))\nprint(isinstance(b, tuple))\nprint(isinstance(c, dict))\n```\n:::\n\n:::cell\nA dataclass is a normal class with `__init__` and `__repr__` generated from the annotated fields. Instances are mutable, support attribute access, and can carry methods like any other class.\n\n```python\nfrom dataclasses import dataclass\n\n@dataclass\nclass UserClass:\n name: str\n score: int\n\na = UserClass("Ada", 98)\nprint(a)\na.score = 100\nprint(a.score)\n```\n\n```output\nUserClass(name=\'Ada\', score=98)\n100\n```\n:::\n\n:::cell\nA `NamedTuple` is a tuple subclass with named positions. Instances are immutable, support both `obj.field` and `obj[index]`, and the helper `_replace` produces a modified copy without mutating the original (since assigning to a field would fail).\n\n```python\nfrom typing import NamedTuple\n\nclass UserTuple(NamedTuple):\n name: str\n score: int\n\nb = UserTuple("Ada", 98)\nprint(b)\nprint(b.name, b[1])\nprint(b._replace(score=100))\n```\n\n```output\nUserTuple(name=\'Ada\', score=98)\nAda 98\nUserTuple(name=\'Ada\', score=100)\n```\n:::\n\n:::cell\nA `TypedDict` is a plain dictionary at runtime. The annotations exist only for the type checker, so the value behaves like any `dict` — useful for JSON-shaped data that crosses an API boundary as a mapping.\n\n```python\nfrom typing import TypedDict\n\nclass UserDict(TypedDict):\n name: str\n score: int\n\nc: UserDict = {"name": "Ada", "score": 98}\nprint(c)\nprint(c["name"])\nprint(type(c).__name__)\n```\n\n```output\n{\'name\': \'Ada\', \'score\': 98}\nAda\ndict\n```\n:::\n\n:::cell\nSame record, three runtime identities. The dataclass is its own class. The `NamedTuple` is literally a tuple. The `TypedDict` is literally a dict. That difference drives the choice: pick the form whose runtime behavior matches what the rest of the program already expects.\n\n```python\nprint(isinstance(a, UserClass))\nprint(isinstance(b, tuple))\nprint(isinstance(c, dict))\n```\n\n```output\nTrue\nTrue\nTrue\n```\n:::\n\n:::note\n- `@dataclass` — mutable, attribute access, methods; good default when behavior travels with data.\n- `typing.NamedTuple` — immutable, attribute + index access, tuple semantics; good for small records that flow through unpacking.\n- `typing.TypedDict` — runtime is `dict`, schema is type-checker-only; good for JSON-shaped data.\n- `collections.namedtuple` is the older, untyped form of `NamedTuple`; prefer the `typing` version in new code.\n:::\n', 'subprocesses.md': '+++\nslug = "subprocesses"\ntitle = "Subprocesses"\nsection = "Standard Library"\nsummary = "subprocess runs external commands with explicit arguments and captured outputs."\ndoc_path = "/library/subprocess.html"\nsee_also = [\n "virtual-environments",\n "networking",\n "threads-and-processes",\n]\nexpected_output = "child process\\n0\\n"\n+++\n\n`subprocess` is the standard boundary for running external commands. It starts another program, waits for it, and gives you a result object with the exit code and captured output.\n\nIn standard Python this is the right tool for calling Git, compilers, shells, or another Python interpreter. This site\'s live example runner does not expose an operating-system process table, so the page teaches the proper `subprocess.run()` contract and labels the runner boundary instead of pretending the command can run here.\n\nUse a list of arguments when possible, capture output when the parent program needs to inspect it, and treat a non-zero return code as a failure. The important boundary is between Python objects and the operating system: Python prepares arguments and environment, then the child program reports back through streams and an exit status.\n\n:::program\n```python\nimport subprocess\nimport sys\n\nresult = subprocess.run(\n [sys.executable, "-c", "print(\'child process\')"],\n text=True,\n capture_output=True,\n check=True,\n)\n\nprint(result.stdout.strip())\nprint(result.returncode)\n```\n:::\n\n:::cell\n`subprocess.run()` spawns a child Python interpreter and waits for it: `capture_output=True` stores the child\'s stdout and stderr on the result, `text=True` decodes them as strings, and `check=True` raises `CalledProcessError` on a non-zero exit. The result object carries the captured streams and exit code as portable evidence the child ran. The in-browser Run button cannot spawn processes, so pressing Run here fails in the sandbox; the output below was produced by really spawning the child under standard CPython when the example was verified.\n\n```python\nimport subprocess\nimport sys\n\nresult = subprocess.run(\n [sys.executable, "-c", "print(\'child process\')"],\n text=True,\n capture_output=True,\n check=True,\n)\n\nprint(result.stdout.strip())\nprint(result.returncode)\n```\n\n```output\nchild process\n0\n```\n:::\n\n:::note\n- Use a list of arguments instead of shell strings when possible.\n- Capture output when the parent program needs to inspect it.\n- `check=True` turns non-zero exits into exceptions.\n- The verified output came from a real child process under standard CPython at build time; the in-browser sandbox has no process table, so live runs of this page fail there.\n:::\n', 'testing.md': '+++\nslug = "testing"\ntitle = "Testing"\nsection = "Standard Library"\nsummary = "Tests make expected behavior executable and repeatable."\ndoc_path = "/library/unittest.html"\nsee_also = [\n "assertions",\n "exceptions",\n "modules",\n]\n+++\n\nTests turn expected behavior into code that can be run again. The useful unit is usually a small example of behavior with clear input, action, and assertion.\n\nPython\'s `unittest` library provides test cases, assertions, suites, and runners. Projects often use `pytest` for ergonomics, but the same idea remains: a test names behavior and fails when the behavior changes.\n\nA broad testing practice also includes fixtures, integration tests, property tests, and coverage. This example stays on the smallest standard-library loop: define behavior, assert the result, run the suite, inspect success.\n\n:::program\n```python\nimport io\nimport unittest\n\n\ndef add(left, right):\n return left + right\n\n\ndef divide(left, right):\n if right == 0:\n raise ZeroDivisionError("denominator is zero")\n return left / right\n\n\nclass AddTests(unittest.TestCase):\n def setUp(self):\n self.zero = 0\n\n def test_adds_numbers(self):\n self.assertEqual(add(self.zero + 2, 3), 5)\n\n def test_adds_empty_strings(self):\n self.assertEqual(add("", "py"), "py")\n\n def test_divide_by_zero_raises(self):\n with self.assertRaises(ZeroDivisionError):\n divide(1, 0)\n\nloader = unittest.defaultTestLoader\nsuite = loader.loadTestsFromTestCase(AddTests)\nstream = io.StringIO()\nrunner = unittest.TextTestRunner(stream=stream, verbosity=0)\nresult = runner.run(suite)\nprint(result.testsRun)\nprint(result.wasSuccessful())\n```\n:::\n\n:::cell\nA test starts with behavior small enough to name. The function can be ordinary code; the test supplies a representative input and expected result.\n\n```python\ndef add(left, right):\n return left + right\n\nprint(add(2, 3))\n```\n\n```output\n5\n```\n:::\n\n:::cell\n`unittest.TestCase` groups test methods. `setUp` runs before each test method to build per-test fixtures, `assertEqual` checks values, and `assertRaises` asserts that a block raises the expected exception type.\n\n```python\nimport unittest\n\n\ndef divide(left, right):\n if right == 0:\n raise ZeroDivisionError("denominator is zero")\n return left / right\n\n\nclass AddTests(unittest.TestCase):\n def setUp(self):\n self.zero = 0\n\n def test_adds_numbers(self):\n self.assertEqual(add(self.zero + 2, 3), 5)\n\n def test_adds_empty_strings(self):\n self.assertEqual(add("", "py"), "py")\n\n def test_divide_by_zero_raises(self):\n with self.assertRaises(ZeroDivisionError):\n divide(1, 0)\n\nprint([name for name in dir(AddTests) if name.startswith("test_")])\n```\n\n```output\n[\'test_adds_empty_strings\', \'test_adds_numbers\', \'test_divide_by_zero_raises\']\n```\n:::\n\n:::cell\nA runner executes the suite and records whether every assertion passed. Capturing the runner stream keeps this page\'s output deterministic.\n\n```python\nimport io\n\nloader = unittest.defaultTestLoader\nsuite = loader.loadTestsFromTestCase(AddTests)\nstream = io.StringIO()\nrunner = unittest.TextTestRunner(stream=stream, verbosity=0)\nresult = runner.run(suite)\nprint(result.testsRun)\nprint(result.wasSuccessful())\n```\n\n```output\n3\nTrue\n```\n:::\n\n:::note\n- Test method names should describe behavior, not implementation details.\n- A good unit test is deterministic and independent of test order.\n- Use broader integration tests when the behavior depends on several components working together.\n:::\n', 'threads-and-processes.md': '+++\nslug = "threads-and-processes"\ntitle = "Threads and Processes"\nsection = "Standard Library"\nsummary = "Threads share memory, while processes run in separate interpreters."\ndoc_path = "/library/concurrent.futures.html"\nsee_also = [\n "async-await",\n "subprocesses",\n "networking",\n]\nexpected_output = "[1, 4, 9]\\nProcessPoolExecutor\\n"\n+++\n\nThreads and processes are two ways to run work outside the current control path. Threads are useful for overlapping I/O-shaped waits, while processes are useful when CPU-bound work needs separate interpreter processes.\n\nIn standard Python, `ThreadPoolExecutor` and `ProcessPoolExecutor` are the ordinary tools for this lesson. This site\'s live example runner does not expose native threads or child processes, so this page keeps the proper executor model visible and separates the standard Python idea from what can execute here.\n\nThis is different from `asyncio`: threads and processes run ordinary callables through executors, while `async` code cooperatively awaits coroutines. Choose the smallest concurrency model that matches the bottleneck.\n\n:::program\n```python\nfrom concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor\n\n\ndef square(number):\n return number * number\n\nwith ThreadPoolExecutor(max_workers=2) as pool:\n print(list(pool.map(square, [1, 2, 3])))\n\nprint(ProcessPoolExecutor.__name__)\n```\n:::\n\n:::cell\n`ThreadPoolExecutor` runs `square` across worker threads that share this interpreter and its GIL; `map()` returns results in input order, and the `with` block joins the workers when the body exits. The in-browser sandbox cannot create native threads, so pressing Run here fails; this thread-pool output was produced under standard CPython at build time.\n\n```python\nfrom concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor\n\n\ndef square(number):\n return number * number\n\nwith ThreadPoolExecutor(max_workers=2) as pool:\n print(list(pool.map(square, [1, 2, 3])))\n```\n\n```output\n[1, 4, 9]\n```\n:::\n\n:::cell\n`ProcessPoolExecutor` is the heavier boundary: separate Python processes with isolated memory, for CPU-bound work that the GIL would otherwise serialise. The sandbox cannot spawn processes either, so this cell only inspects the class name rather than running a pool.\n\n```python\nprint(ProcessPoolExecutor.__name__)\n```\n\n```output\nProcessPoolExecutor\n```\n:::\n\n:::note\n- Threads share memory, so mutable shared state needs care.\n- Processes avoid shared interpreter state but require values to cross a process boundary.\n- Prefer `asyncio` for coroutine-based I/O and executors for ordinary blocking callables.\n- The thread-pool output came from real worker threads under standard CPython at build time; the in-browser sandbox cannot create threads or processes, so live runs of this page fail there.\n:::\n', 'truth-and-size.md': '+++\nslug = "truth-and-size"\ntitle = "Truth and Size"\nsection = "Data Model"\nsummary = "__bool__ and __len__ decide how objects behave in truth tests and len()."\ndoc_path = "/reference/datamodel.html#object.__bool__"\nsee_also = [\n "truthiness",\n "special-methods",\n "container-protocols",\n]\n+++\n\nTruth tests ask an object whether it should count as true. Containers usually answer through their size, while domain objects can answer with `__bool__` when emptiness is not the right idea.\n\n`__len__` supports `len(obj)` and also provides a fallback truth value: length zero is false, non-zero length is true. `__bool__` is more direct and wins when both are present.\n\nUse these methods to match the meaning of your object. A queue can be false when it has no items; an account might be true only when it is active, regardless of its balance.\n\n:::program\n```python\nclass Inbox:\n def __init__(self, messages):\n self.messages = list(messages)\n\n def __len__(self):\n return len(self.messages)\n\nclass Account:\n def __init__(self, active):\n self.active = active\n\n def __bool__(self):\n return self.active\n\nprint(len(Inbox(["hi", "bye"])))\nprint(bool(Inbox([])))\nprint(bool(Account(False)))\n```\n:::\n\n:::cell\n`__len__` lets `len()` ask an object for its size.\n\n```python\nclass Inbox:\n def __init__(self, messages):\n self.messages = list(messages)\n\n def __len__(self):\n return len(self.messages)\n\nprint(len(Inbox(["hi", "bye"])))\n```\n\n```output\n2\n```\n:::\n\n:::cell\nIf a class has `__len__` but no `__bool__`, Python uses zero length as false.\n\n```python\nprint(bool(Inbox([])))\n```\n\n```output\nFalse\n```\n:::\n\n:::cell\n`__bool__` expresses truth directly when the answer is not just container size.\n\n```python\nclass Account:\n def __init__(self, active):\n self.active = active\n\n def __bool__(self):\n return self.active\n\nprint(bool(Account(False)))\n```\n\n```output\nFalse\n```\n:::\n\n:::note\n- Prefer `__len__` for sized containers.\n- Prefer `__bool__` when truth has domain meaning.\n- Keep truth tests unsurprising; surprising falsy objects make conditionals harder to read.\n:::\n', 'truthiness.md': '+++\nslug = "truthiness"\ntitle = "Truthiness"\nsection = "Basics"\nsummary = "Python conditions use truthiness, not only explicit booleans."\ndoc_path = "/library/stdtypes.html#truth-value-testing"\nsee_also = [\n "booleans",\n "none",\n "conditionals",\n "special-methods",\n]\n+++\n\nTruthiness is one of Python\'s most important conveniences: conditions can test objects directly instead of requiring explicit boolean comparisons everywhere.\n\nEmpty containers, numeric zero, None, and False are false; most other values are true. This makes common checks such as if items: concise and idiomatic.\n\nUse truthiness when it reads naturally, but choose explicit comparisons when the distinction matters, such as checking whether a value is exactly None.\n\n:::program\n```python\nitems = []\nname = "Ada"\n\nif not items:\n print("no items")\n\nif name:\n print("has a name")\n\nprint(bool(0))\nprint(bool(42))\n```\n:::\n\n:::cell\nAn empty list is false, so `not items` reads as "items is empty". The condition tests the object directly — no `len(items) == 0` comparison is needed.\n\n```python\nitems = []\nname = "Ada"\n\nif not items:\n print("no items")\n```\n\n```output\nno items\n```\n:::\n\n:::cell\nA non-empty string is true, so `if name:` asks "did we get a name?" in one word. Reach for an explicit comparison instead when the distinction matters — `if name is not None:` treats an empty string differently from a missing one.\n\n```python\nif name:\n print("has a name")\n```\n\n```output\nhas a name\n```\n:::\n\n:::cell\n`bool()` reveals the truth value any condition would use. Zero-like numbers convert to `False`; other numbers convert to `True`.\n\n```python\nprint(bool(0))\nprint(bool(42))\n```\n\n```output\nFalse\nTrue\n```\n:::\n\n:::note\n- Empty containers and zero-like numbers are false in conditions.\n- Use explicit comparisons when they communicate intent better than truthiness.\n:::\n', 'tuples.md': '+++\nslug = "tuples"\ntitle = "Tuples"\nsection = "Collections"\nsummary = "Tuples group a fixed number of positional values."\ndoc_path = "/tutorial/datastructures.html#tuples-and-sequences"\nsee_also = [\n "lists",\n "unpacking",\n "structured-data-shapes",\n]\n+++\n\nTuples are ordered, immutable sequences. They exist for small fixed groups where position has meaning: coordinates, RGB colors, database rows, and multiple return values.\n\nUse lists for variable-length collections of similar items. Use tuples when the number of positions is part of the data shape and unpacking can give each position a useful name.\n\nBecause tuples are immutable, you cannot append or replace positions in place. If the shape needs to grow or change, a list or dataclass is usually a better fit.\n\n:::program\n```python\npoint = (3, 4)\nx, y = point\nprint(x + y)\n\nred = (255, 0, 0)\nprint(red[0])\nprint(len(red))\n\nrecord = ("Ada", 10)\nname, score = record\nprint(f"{name}: {score}")\n\nscores = [10, 9, 8]\nscores.append(7)\nprint(scores)\n\nstudent = ("Ada", 2024, "math")\nname, year, subject = student\nprint(name, year, subject)\n```\n:::\n\n:::cell\nUse a tuple for a fixed-size record where each position has a known meaning. Unpacking turns those positions into names at the point of use.\n\n```python\npoint = (3, 4)\nx, y = point\nprint(x + y)\n```\n\n```output\n7\n```\n:::\n\n:::cell\nTuples are sequences, so indexing and `len()` work. They are different from lists because their length and item references are fixed after creation.\n\n```python\nred = (255, 0, 0)\nprint(red[0])\nprint(len(red))\n```\n\n```output\n255\n3\n```\n:::\n\n:::cell\nTuples pair naturally with multiple return values and unpacking. If the fields need names everywhere, graduate to a dataclass or named tuple.\n\n```python\nrecord = ("Ada", 10)\nname, score = record\nprint(f"{name}: {score}")\n```\n\n```output\nAda: 10\n```\n:::\n\n:::cell\nLists and tuples carry different intent. A list holds a variable number of similar items and grows with `append`; a tuple has a fixed shape where each position has its own meaning, and unpacking gives those positions names.\n\n```python\nscores = [10, 9, 8]\nscores.append(7)\nprint(scores)\n\nstudent = ("Ada", 2024, "math")\nname, year, subject = student\nprint(name, year, subject)\n```\n\n```output\n[10, 9, 8, 7]\nAda 2024 math\n```\n:::\n\n:::note\n- Tuples are immutable sequences with fixed length.\n- Use tuples for small records where position has meaning.\n- Use lists for variable-length collections of similar items.\n- Reach for a dataclass or `NamedTuple` when fields deserve names everywhere they\'re used.\n:::\n', 'type-aliases.md': '+++\nslug = "type-aliases"\ntitle = "Type Aliases"\nsection = "Types"\nsummary = "Type aliases give a meaningful name to a repeated type shape."\ndoc_path = "/library/typing.html#type-aliases"\nsee_also = [\n "type-hints",\n "newtype",\n "union-and-optional-types",\n]\n+++\n\nA type alias gives a name to an annotation shape. It helps readers and type checkers understand the role of a value without repeating a long type expression everywhere.\n\nPython 3.13 supports the `type` statement for explicit aliases. Older assignment-style aliases still appear in code, but the `type` statement makes the intent clear and creates a `TypeAliasType` object at runtime.\n\nAn alias does not create a new runtime type. If you need a static distinction between compatible values such as user IDs and order IDs, use `NewType` instead.\n\n:::program\n```python\ntype UserId = int\ntype Scores = dict[UserId, int]\nLegacyName = str\n\n\ndef best_user(scores: Scores) -> UserId:\n return max(scores, key=scores.get)\n\nscores: Scores = {1: 98, 2: 91}\nprint(best_user(scores))\nprint(UserId.__name__)\nprint(LegacyName("Ada"))\n```\n:::\n\n:::cell\nThe `type` statement names an annotation shape. Here `Scores` means a dictionary from user IDs to integer scores.\n\n```python\ntype UserId = int\ntype Scores = dict[UserId, int]\n\n\ndef best_user(scores: Scores) -> UserId:\n return max(scores, key=scores.get)\n\nscores: Scores = {1: 98, 2: 91}\nprint(best_user(scores))\n```\n\n```output\n1\n```\n:::\n\n:::cell\nModern aliases are runtime objects that keep their alias name for introspection.\n\n```python\nprint(UserId.__name__)\nprint(Scores.__name__)\n```\n\n```output\nUserId\nScores\n```\n:::\n\n:::cell\nAssignment-style aliases are still common, but they are just ordinary names bound to existing objects.\n\n```python\nLegacyName = str\nprint(LegacyName("Ada"))\nprint(LegacyName is str)\n```\n\n```output\nAda\nTrue\n```\n:::\n\n:::note\n- Use aliases to name repeated or domain-specific annotation shapes.\n- A type alias does not validate values at runtime.\n- Use `NewType` when two values share a runtime representation but should not be mixed statically.\n:::\n', 'type-hints.md': '+++\nslug = "type-hints"\ntitle = "Type Hints"\nsection = "Types"\nsummary = "Annotations document expected types and power static analysis."\ndoc_path = "/library/typing.html"\nsee_also = [\n "union-and-optional-types",\n "type-aliases",\n "generics-and-typevar",\n "runtime-type-checks",\n]\n+++\n\nType hints are annotations that document expected shapes for values, parameters, and return results. They exist so tools and readers can understand API boundaries before the program runs.\n\nPython stores many annotations but does not enforce most of them at runtime. Use type hints for communication and static analysis; use validation or exceptions when runtime checks are required.\n\nThe alternative to an annotation is prose, tests, or runtime validation. Good Python code often uses all three at important boundaries.\n\n:::program\n```python\ndef total(numbers: list[int]) -> int:\n return sum(numbers)\n\nprint(total([1, 2, 3]))\nprint(total.__annotations__)\n\n\ndef label(score: int) -> str:\n return f"score={score}"\n\nprint(label("high"))\n\n\ndef find(name: str, options: list[str]) -> str | None:\n return name if name in options else None\n\nprint(find("Ada", ["Ada", "Grace"]))\nprint(find("Guido", ["Ada", "Grace"]))\n\n\nfrom typing import Optional\n\ndef lookup(name: str) -> Optional[int]:\n table = {"Ada": 1815, "Grace": 1906}\n return table.get(name)\n\nprint(lookup("Ada"))\nprint(lookup("Guido"))\n\n\ntype Score = int\n\ndef grade(score: Score) -> str:\n return "pass" if score >= 50 else "fail"\n\nprint(grade(72))\n```\n:::\n\n:::cell\nType hints document expected parameter and return shapes. Python still runs the function normally at runtime.\n\n```python\ndef total(numbers: list[int]) -> int:\n return sum(numbers)\n\nprint(total([1, 2, 3]))\n```\n\n```output\n6\n```\n:::\n\n:::cell\nPython stores annotations on the function object for tools and introspection. Type checkers use this information without changing the function call syntax.\n\n```python\nprint(total.__annotations__)\n```\n\n```output\n{\'numbers\': list[int], \'return\': <class \'int\'>}\n```\n:::\n\n:::cell\nMost hints are not runtime validation. This call passes a string where the hint says `int`; Python still calls the function because the body can format any value.\n\n```python\ndef label(score: int) -> str:\n return f"score={score}"\n\nprint(label("high"))\n```\n\n```output\nscore=high\n```\n:::\n\n:::cell\nUse `X | Y` (PEP 604) to express "either type". `str | None` says the result is a string or absent. `typing.Optional[X]` is the older, still-supported spelling for the same idea — `Optional[X]` is equivalent to `X | None`.\n\n```python\ndef find(name: str, options: list[str]) -> str | None:\n return name if name in options else None\n\nprint(find("Ada", ["Ada", "Grace"]))\nprint(find("Guido", ["Ada", "Grace"]))\n\n\nfrom typing import Optional\n\ndef lookup(name: str) -> Optional[int]:\n table = {"Ada": 1815, "Grace": 1906}\n return table.get(name)\n\nprint(lookup("Ada"))\nprint(lookup("Guido"))\n```\n\n```output\nAda\nNone\n1815\nNone\n```\n:::\n\n:::cell\nThe `type` statement names a type so it can be reused with intent. `type Score = int` keeps the underlying type at runtime but lets the API talk about a domain concept rather than a primitive. Older code spells this `Score: TypeAlias = int`; `typing.TypeAlias` is deprecated since Python 3.12, and the type-aliases page covers the modern statement in depth.\n\n```python\ntype Score = int\n\ndef grade(score: Score) -> str:\n return "pass" if score >= 50 else "fail"\n\nprint(grade(72))\n```\n\n```output\npass\n```\n:::\n\n:::note\n- Python does not enforce most type hints at runtime.\n- Tools like type checkers and editors use annotations to catch mistakes earlier.\n- Use `X | Y` for unions and `Optional[X]` for "X or None"; both spellings mean the same thing.\n- Reach for a `type` alias when a domain name reads better than a raw primitive type.\n- Use runtime validation when untrusted input must be rejected while the program runs.\n:::\n', 'typed-dicts.md': '+++\nslug = "typed-dicts"\ntitle = "TypedDict"\nsection = "Types"\nsummary = "TypedDict describes dictionaries with known string keys."\ndoc_path = "/library/typing.html#typing.TypedDict"\nsee_also = [\n "dicts",\n "json",\n "dataclasses",\n "structured-data-shapes",\n]\n+++\n\n`TypedDict` describes dictionary records with known keys. It is useful for JSON-like data that should remain a dictionary instead of becoming a class instance.\n\nThe important boundary is static versus runtime behavior. Type checkers can know that `name` is a string and `score` is an integer, but at runtime the value is still an ordinary `dict`.\n\nUse `TypedDict` for external records and `dataclass` when your own program wants attribute access, methods, and construction behavior.\n\n:::program\n```python\nfrom typing import NotRequired, TypedDict\n\nclass User(TypedDict):\n name: str\n score: int\n nickname: NotRequired[str]\n\n\ndef describe(user: User) -> str:\n return f"{user[\'name\']}: {user[\'score\']}"\n\nrecord: User = {"name": "Ada", "score": 98}\nprint(describe(record))\nprint(isinstance(record, dict))\nprint(record.get("nickname", "none"))\n```\n:::\n\n:::cell\nUse `TypedDict` for JSON-like records that remain dictionaries.\n\n```python\nfrom typing import TypedDict\n\nclass User(TypedDict):\n name: str\n score: int\n\n\ndef describe(user: User) -> str:\n return f"{user[\'name\']}: {user[\'score\']}"\n\nrecord: User = {"name": "Ada", "score": 98}\nprint(describe(record))\n```\n\n```output\nAda: 98\n```\n:::\n\n:::cell\nAt runtime, a `TypedDict` value is still a plain dictionary.\n\n```python\nprint(isinstance(record, dict))\nprint(type(record).__name__)\n```\n\n```output\nTrue\ndict\n```\n:::\n\n:::cell\n`NotRequired` marks a key that type checkers should treat as optional. Runtime lookup still uses normal dictionary tools such as `get()`.\n\n```python\nfrom typing import NotRequired\n\nclass UserWithNickname(TypedDict):\n name: str\n score: int\n nickname: NotRequired[str]\n\nrecord: UserWithNickname = {"name": "Ada", "score": 98}\nprint(record.get("nickname", "none"))\n```\n\n```output\nnone\n```\n:::\n\n:::note\n- Use `TypedDict` for dictionary records from JSON or APIs.\n- Type checkers understand required and optional keys.\n- Runtime behavior is still ordinary dictionary behavior.\n:::\n', 'union-and-optional-types.md': '+++\nslug = "union-and-optional-types"\ntitle = "Union and Optional Types"\nsection = "Types"\nsummary = "The | operator describes values that may have more than one static type."\ndoc_path = "/library/typing.html#typing.Optional"\nsee_also = [\n "none",\n "type-hints",\n "match-statements",\n]\n+++\n\nA union type says that a value may have one of several static shapes. `int | str` means callers may pass either an integer or a string.\n\n`T | None` is the modern spelling for an optional value. The annotation documents that absence is expected, but the code still needs to handle `None` before using the non-optional behavior.\n\nUnions are useful at boundaries where input is flexible. Inside a function, narrow the value with an `is None`, `isinstance()`, or pattern check so the rest of the code has one clear shape.\n\n:::program\n```python\ndef label(value: int | str) -> str:\n return f"item-{value}"\n\n\ndef greeting(name: str | None) -> str:\n if name is None:\n return "hello guest"\n return f"hello {name.upper()}"\n\nprint(label(3))\nprint(label("A"))\nprint(greeting(None))\nprint(greeting("Ada"))\nprint(greeting.__annotations__)\n```\n:::\n\n:::cell\nUse `A | B` when a value may have either type. The function body should use operations that make sense for every member of the union.\n\n```python\ndef label(value: int | str) -> str:\n return f"item-{value}"\n\nprint(label(3))\nprint(label("A"))\n```\n\n```output\nitem-3\nitem-A\n```\n:::\n\n:::cell\n`str | None` means the function accepts either a string or explicit absence. Check for `None` before calling string methods.\n\n```python\ndef greeting(name: str | None) -> str:\n if name is None:\n return "hello guest"\n return f"hello {name.upper()}"\n\nprint(greeting(None))\nprint(greeting("Ada"))\n```\n\n```output\nhello guest\nhello ADA\n```\n:::\n\n:::cell\nUnion annotations are visible at runtime, but Python does not enforce them when the function is called.\n\n```python\nprint(greeting.__annotations__)\n```\n\n```output\n{\'name\': str | None, \'return\': <class \'str\'>}\n```\n:::\n\n:::note\n- Use `A | B` when a value may have either type.\n- `T | None` means absence is an expected case, not an error by itself.\n- Narrow unions before using behavior that belongs to only one member type.\n:::\n', 'unpacking.md': '+++\nslug = "unpacking"\ntitle = "Unpacking"\nsection = "Collections"\nsummary = "Unpacking binds names from sequences and mappings concisely."\ndoc_path = "/tutorial/datastructures.html#tuples-and-sequences"\nsee_also = [\n "tuples",\n "multiple-return-values",\n "args-and-kwargs",\n "dicts",\n]\n+++\n\nUnpacking binds multiple names from one iterable or mapping. It makes the structure of data visible at the point where values are introduced.\n\nStarred unpacking handles variable-length sequences by collecting the middle or remaining values. This keeps common head-tail patterns readable.\n\nDictionary unpacking with ** connects structured data to function calls. It is widely used in configuration, adapters, and code that bridges APIs.\n\n:::program\n```python\npoint = (3, 4)\nx, y = point\nprint(x, y)\n\nfirst, *middle, last = [1, 2, 3, 4]\nprint(first, middle, last)\n\ndef describe(name, language):\n print(name, language)\n\ndata = {"name": "Ada", "language": "Python"}\ndescribe(**data)\n```\n:::\n\n:::cell\nTuple unpacking assigns each position to a name in one statement: `x` receives the first element of `point` and `y` the second. The assignment fails loudly if the number of names and elements disagree, which catches shape mistakes early.\n\n```python\npoint = (3, 4)\nx, y = point\nprint(x, y)\n```\n\n```output\n3 4\n```\n:::\n\n:::cell\nThe starred name collects however many elements the head and tail don\'t claim — here `first` and `last` take the ends and `*middle` gathers the rest into a list. The same list works whether it has four elements or forty.\n\n```python\nfirst, *middle, last = [1, 2, 3, 4]\nprint(first, middle, last)\n```\n\n```output\n1 [2, 3] 4\n```\n:::\n\n:::cell\n`describe(**data)` spreads the dictionary\'s keys as keyword arguments, so the call site never repeats `name=` and `language=` by hand. This is the bridge between dict-shaped data (configuration, parsed JSON) and function signatures.\n\n```python\ndef describe(name, language):\n print(name, language)\n\ndata = {"name": "Ada", "language": "Python"}\ndescribe(**data)\n```\n\n```output\nAda Python\n```\n:::\n\n:::note\n- Starred unpacking collects the remaining values into a list.\n- Dictionary unpacking with ** is common when calling functions with structured data.\n- Prefer indexing when you need one position; prefer unpacking when naming several positions makes the shape clearer.\n:::\n', 'values.md': '+++\nslug = "values"\ntitle = "Values"\nsection = "Basics"\nsummary = "Python programs evaluate expressions into objects such as text, numbers, booleans, and None."\ndoc_path = "/library/stdtypes.html"\nsee_also = [\n "variables",\n "booleans",\n "none",\n "literals",\n]\n+++\n\nA Python program works by evaluating expressions into values. Values are objects: text, integers, floats, booleans, `None`, and many richer types introduced later.\n\nNames point to values; they are not declarations that permanently fix a type. Operations usually produce new values, which you can print, store, compare, or pass to functions.\n\nThis page is a map, not the whole territory. Later pages explain the boundaries: equality vs identity, mutable vs immutable values, truthiness vs literal booleans, and `None` vs a missing key or an exception.\n\n:::program\n```python\ntext = "python"\ncount = 3\nratio = 2.5\nready = True\nmissing = None\n\nprint(type(text).__name__)\nprint(text.upper())\nprint(count + 4)\nprint(ratio * 2)\n\nprint(ready and count > 0)\nprint(missing is None)\n```\n:::\n\n:::cell\nStart with several built-in values. Python does not require declarations before binding these names, and each value is still an object with a type.\n\n```python\ntext = "python"\ncount = 3\nratio = 2.5\nready = True\nmissing = None\n\nprint(type(text).__name__)\n```\n\n```output\nstr\n```\n:::\n\n:::cell\nMethods and operators evaluate to new values. The original `text`, `count`, and `ratio` bindings remain ordinary objects you can reuse.\n\n```python\nprint(text.upper())\nprint(count + 4)\nprint(ratio * 2)\n```\n\n```output\nPYTHON\n7\n5.0\n```\n:::\n\n:::cell\nBoolean expressions combine facts, and `None` is checked by identity because it is a singleton absence marker.\n\n```python\nprint(ready and count > 0)\nprint(missing is None)\n```\n\n```output\nTrue\nTrue\n```\n:::\n\n:::note\n- Values are objects; names point to them and operations usually create new values.\n- Use `is None` for the absence marker, not `== None`.\n- This overview introduces boundaries that later pages explain in detail.\n:::\n', 'variables.md': '+++\nslug = "variables"\ntitle = "Variables"\nsection = "Basics"\nsummary = "Names are bound to values with assignment."\ndoc_path = "/reference/simple_stmts.html#assignment-statements"\nsee_also = [\n "values",\n "mutability",\n "object-lifecycle",\n "constants",\n]\n+++\n\nPython variables are names bound to objects. Assignment creates or rebinds a name; it does not require a declaration and it does not permanently attach a type to the name.\n\nRebinding changes which object a name refers to. Augmented assignment such as `+=` is the idiomatic way to update counters and accumulators.\n\nUse clear names for values that matter later. Python\'s flexibility makes naming more important, not less.\n\nUse assignment when a value needs a name for reuse or explanation. Prefer a direct expression when naming the intermediate value would add noise.\n\n:::program\n```python\nmessage = "hi"\nprint(message)\n\nmessage = "hello"\nprint(message)\n\ncount = 3\ncount += 1\nprint(count)\n```\n:::\n\n:::cell\nAssignment binds a name to a value. Once bound, the name can be used anywhere that value is needed.\n\n```python\nmessage = "hi"\nprint(message)\n```\n\n```output\nhi\n```\n:::\n\n:::cell\nAssignment can rebind the same name to a different value. The name is not permanently attached to the first object.\n\n```python\nmessage = "hello"\nprint(message)\n```\n\n```output\nhello\n```\n:::\n\n:::cell\nAugmented assignment reads the current binding, computes an updated value, and stores the result back under the same name.\n\n```python\ncount = 3\ncount += 1\nprint(count)\n```\n\n```output\n4\n```\n:::\n\n:::note\n- Python variables are names bound to objects, not boxes with fixed types.\n- Rebinding a name is normal.\n- Use augmented assignment for counters and accumulators.\n:::\n', 'virtual-environments.md': '+++\nslug = "virtual-environments"\ntitle = "Virtual Environments"\nsection = "Modules"\nsummary = "Virtual environments isolate a project\'s Python packages."\ndoc_path = "/library/venv.html"\nsee_also = [\n "packages",\n "modules",\n "import-aliases",\n]\nexpected_output = ".venv\\nTrue\\n"\n+++\n\nVirtual environments isolate a project\'s installed packages from the global Python installation and from other projects. The usual workflow is a command-line one: create `.venv`, activate it, then install project dependencies there.\n\nIn standard Python, `python -m venv .venv` is the everyday command. This site\'s live example runner is built from declared dependencies rather than an activated shell environment, so the runnable part keeps to deterministic evidence while the page still teaches the standard-Python workflow.\n\nA virtual environment changes installation and import paths. It does not change the Python language, package layout rules, or module names.\n\n:::program\n```python\nimport pathlib\nimport tempfile\nimport venv\n\nwith tempfile.TemporaryDirectory() as directory:\n env_path = pathlib.Path(directory) / ".venv"\n builder = venv.EnvBuilder(with_pip=False)\n builder.create(env_path)\n\n config = (env_path / "pyvenv.cfg").read_text()\n print(env_path.name)\n print("home" in config)\n```\n:::\n\n:::unsupported\nThe standard project setup command is `python -m venv .venv`. It creates a directory with its own interpreter entry points and package install location. After activation, `python -m pip install ...` installs into that environment rather than into another project. (This workflow is for standard Python projects. The Python By Example runner is deployed from declared dependencies instead of an activated shell environment.)\n\n```python\nimport subprocess\nimport sys\n\nsubprocess.run([sys.executable, "-m", "venv", ".venv"], check=True)\nsubprocess.run([".venv/bin/python", "-m", "pip", "install", "requests"], check=True)\n```\n:::\n\n:::cell\n`venv.EnvBuilder` exposes the same environment-creation mechanism as `python -m venv`. A temporary directory keeps the example from leaving project files behind.\n\n```python\nimport pathlib\nimport tempfile\nimport venv\n\nwith tempfile.TemporaryDirectory() as directory:\n env_path = pathlib.Path(directory) / ".venv"\n builder = venv.EnvBuilder(with_pip=False)\n builder.create(env_path)\n\n config = (env_path / "pyvenv.cfg").read_text()\n print(env_path.name)\n print("home" in config)\n```\n\n```output\n.venv\nTrue\n```\n:::\n\n:::note\n- Use `python -m venv .venv` for everyday standard-Python project setup.\n- A venv isolates installed packages; it does not change how imports are written.\n- This site\'s runner uses a deployment dependency model, not an activated shell environment.\n- That runner constraint is separate from the standard Python `venv` workflow you would use in local projects.\n:::\n', 'warnings.md': '+++\nslug = "warnings"\ntitle = "Warnings"\nsection = "Errors"\nsummary = "warnings report soft problems without immediately stopping the program."\ndoc_path = "/library/warnings.html"\nsee_also = [\n "exceptions",\n "logging",\n "testing",\n]\n+++\n\nA warning reports a problem that callers should know about, but it does not have to stop the current operation. Deprecations are the classic case: the old API can still return a value while telling users to migrate.\n\nWarnings sit between logging and exceptions. Logging records operational evidence; exceptions stop the current path; warnings make compatibility or correctness concerns visible according to a filter.\n\nTests often capture warnings so deprecations are asserted instead of merely printed. Filters can also turn warnings into errors when a project wants to enforce cleanup.\n\n:::program\n```python\nimport warnings\n\n\ndef old_name():\n warnings.warn("old_name is deprecated", DeprecationWarning, stacklevel=2)\n return "result"\n\nwith warnings.catch_warnings(record=True) as caught:\n warnings.simplefilter("always", DeprecationWarning)\n print(old_name())\n print(caught[0].category.__name__)\n print(str(caught[0].message))\n\nwith warnings.catch_warnings():\n warnings.simplefilter("error", DeprecationWarning)\n try:\n old_name()\n except DeprecationWarning:\n print("warning became error")\n```\n:::\n\n:::cell\nCapture warnings in tests when the returned value still matters but the migration notice must be asserted.\n\n```python\nimport warnings\n\n\ndef old_name():\n warnings.warn("old_name is deprecated", DeprecationWarning, stacklevel=2)\n return "result"\n\nwith warnings.catch_warnings(record=True) as caught:\n warnings.simplefilter("always", DeprecationWarning)\n print(old_name())\n print(caught[0].category.__name__)\n print(str(caught[0].message))\n```\n\n```output\nresult\nDeprecationWarning\nold_name is deprecated\n```\n:::\n\n:::cell\nA filter can promote selected warnings to exceptions, which is useful in CI when deprecated calls should fail the build.\n\n```python\nwith warnings.catch_warnings():\n warnings.simplefilter("error", DeprecationWarning)\n try:\n old_name()\n except DeprecationWarning:\n print("warning became error")\n```\n\n```output\nwarning became error\n```\n:::\n\n:::note\n- Use warnings for soft problems callers can act on later.\n- Use exceptions when the current operation cannot continue.\n- `stacklevel` should point the warning at the caller rather than inside the helper.\n:::\n', 'while-loops.md': '+++\nslug = "while-loops"\ntitle = "While Loops"\nsection = "Control Flow"\nsummary = "while repeats until changing state makes a condition false."\ndoc_path = "/reference/compound_stmts.html#while"\nsee_also = [\n "for-loops",\n "sentinel-iteration",\n "break-and-continue",\n]\n+++\n\nA `while` loop repeats while a condition remains true. Unlike `for`, which consumes an existing iterable, `while` is for state-driven repetition where the next step depends on what happened so far.\n\nThe loop body must make progress toward stopping. That progress might be decrementing a counter, reading until a sentinel value, or waiting until some external state changes.\n\nReach for `for` when you already have values to consume. Reach for `while` when the loop\'s own state decides whether another iteration is needed.\n\n:::program\n```python\nremaining = 3\nwhile remaining > 0:\n print(f"launch in {remaining}")\n remaining -= 1\nprint("liftoff")\n\nresponses = iter(["retry", "retry", "ok"])\nstatus = next(responses)\nwhile status != "ok":\n print(f"status: {status}")\n status = next(responses)\nprint(f"status: {status}")\n```\n:::\n\n:::cell\nUse `while` when the condition, not an iterable, controls repetition. Here the loop owns the countdown state and updates it each time through the body.\n\n```python\nremaining = 3\nwhile remaining > 0:\n print(f"launch in {remaining}")\n remaining -= 1\nprint("liftoff")\n```\n\n```output\nlaunch in 3\nlaunch in 2\nlaunch in 1\nliftoff\n```\n:::\n\n:::cell\nA sentinel loop stops when a special value appears. The loop does not know in advance how many retries it will need; it keeps going until the state says to stop.\n\n```python\nresponses = iter(["retry", "retry", "ok"])\nstatus = next(responses)\nwhile status != "ok":\n print(f"status: {status}")\n status = next(responses)\nprint(f"status: {status}")\n```\n\n```output\nstatus: retry\nstatus: retry\nstatus: ok\n```\n:::\n\n:::note\n- Use `while` when changing state decides whether the loop continues.\n- Update loop state inside the body so the condition can become false.\n- Prefer `for` when you already have a collection, range, iterator, or generator to consume.\n:::\n', 'yield-from.md': '+++\nslug = "yield-from"\ntitle = "Yield From"\nsection = "Iteration"\nsummary = "yield from delegates part of a generator to another iterable."\ndoc_path = "/reference/expressions.html#yield-expressions"\nsee_also = [\n "generators",\n "generator-expressions",\n "itertools",\n]\n+++\n\n`yield from` lets one generator yield every value from another iterable. It is a compact way to delegate part of a stream.\n\nUse it when a generator is mostly stitching together other iterables or sub-generators. It keeps the producer pipeline visible without writing a nested `for` loop.\n\nThe consumer still sees one stream of values.\n\n:::program\n```python\ndef page():\n yield "header"\n yield from ["intro", "body"]\n yield "footer"\n\nprint(list(page()))\n\n\ndef flatten(rows):\n for row in rows:\n yield from row\n\nprint(list(flatten([[1, 2], [3]])))\n```\n:::\n\n:::cell\n`yield from` delegates to another iterable. The caller receives one stream even though part of it came from a list.\n\n```python\ndef page():\n yield "header"\n yield from ["intro", "body"]\n yield "footer"\n\nprint(list(page()))\n```\n\n```output\n[\'header\', \'intro\', \'body\', \'footer\']\n```\n:::\n\n:::cell\nDelegation is useful when flattening nested iterables. `yield from row` replaces an inner loop that would yield each item by hand.\n\n```python\ndef flatten(rows):\n for row in rows:\n yield from row\n\nprint(list(flatten([[1, 2], [3]])))\n```\n\n```output\n[1, 2, 3]\n```\n:::\n\n:::note\n- `yield from iterable` yields each value from that iterable.\n- It keeps generator pipelines compact.\n- Use a plain `yield` when producing one value directly.\n:::\n'}