+1
-1
languages/python/README.md
+1
-1
languages/python/README.md
···
4
4
5
5
python is dynamically typed but increasingly written with static type hints. it has first-class async support but doesn't force it on you. it has a global interpreter lock but that matters less than people think for I/O-bound work.
6
6
7
-
these notes focus on modern python (3.10+) - the patterns that emerge when you take typing seriously and use async where it makes sense.
7
+
these notes assume python 3.12 or later. 3.12 introduced native generic syntax and the `type` statement - the typing story finally feels complete.
8
8
9
9
## the language
10
10
+29
languages/python/patterns.md
+29
languages/python/patterns.md
···
181
181
182
182
the wrapper's signature now includes `_filter`. IDEs and schema generators see it.
183
183
184
+
## argparse for CLIs
185
+
186
+
argparse is stdlib and sufficient for most CLIs. subparsers give you git-style commands:
187
+
188
+
```python
189
+
import argparse
190
+
191
+
def main() -> int:
192
+
parser = argparse.ArgumentParser(description="my tool")
193
+
subparsers = parser.add_subparsers(dest="command")
194
+
195
+
list_parser = subparsers.add_parser("list", aliases=["ls"])
196
+
list_parser.add_argument("collection")
197
+
list_parser.add_argument("--limit", type=int, default=50)
198
+
199
+
args = parser.parse_args()
200
+
201
+
match args.command:
202
+
case "list" | "ls":
203
+
return do_list(args.collection, args.limit)
204
+
case _:
205
+
parser.print_help()
206
+
return 1
207
+
208
+
return 0
209
+
```
210
+
211
+
the pattern: parse once at the top, dispatch based on command, return exit codes.
212
+
184
213
## module-level singletons
185
214
186
215
instantiate once, import everywhere:
+20
-17
languages/python/typing.md
+20
-17
languages/python/typing.md
···
1
1
# typing
2
2
3
-
modern python type hints - not just for IDEs, but for thinking clearly about code.
3
+
python 3.12 made typing feel native. generics have real syntax, type aliases have a keyword, and you rarely need to import from `typing` anymore.
4
4
5
-
## basics
5
+
## the basics
6
6
7
-
use `|` for unions, not `Optional`:
7
+
use `|` for unions:
8
8
9
9
```python
10
-
# yes
11
10
def fetch(url: str, timeout: float | None = None) -> dict[str, Any]: ...
11
+
```
12
+
13
+
`from __future__ import annotations` at the top of files lets you reference types before they're defined and avoids some runtime costs.
14
+
15
+
## type aliases
12
16
13
-
# no
14
-
from typing import Optional
15
-
def fetch(url: str, timeout: Optional[float] = None) -> Dict[str, Any]: ...
17
+
the `type` statement creates type aliases:
18
+
19
+
```python
20
+
type UserId = int
21
+
type Handler = Callable[[Request], Response]
22
+
type Result[T] = T | Error
16
23
```
17
24
18
-
`from __future__ import annotations` at the top of every file. this defers evaluation so you can reference types before they're defined.
25
+
clearer than `TypeAlias` annotations and supports generics directly.
19
26
20
27
## TypedDict
21
28
···
73
80
74
81
## generics
75
82
76
-
for functions that work with any type:
83
+
3.12 introduced native syntax for generics. no more `TypeVar`:
77
84
78
85
```python
79
-
from typing import TypeVar
80
-
81
-
T = TypeVar("T")
82
-
83
-
def first(items: list[T]) -> T | None:
86
+
def first[T](items: list[T]) -> T | None:
84
87
return items[0] if items else None
85
88
```
86
89
···
89
92
```python
90
93
from pydantic import BaseModel
91
94
92
-
ModelT = TypeVar("ModelT", bound=BaseModel)
93
-
94
-
def parse(data: dict, model: type[ModelT]) -> ModelT:
95
+
def parse[M: BaseModel](data: dict, model: type[M]) -> M:
95
96
return model.model_validate(data)
96
97
```
98
+
99
+
the `[T]` declares the type parameter inline. `[M: BaseModel]` constrains it. this is real syntax, not a workaround.
97
100
98
101
## ParamSpec
99
102
+20
languages/python/uv.md
+20
languages/python/uv.md
···
56
56
uv run --with git+https://github.com/prefecthq/prefect.git@branch#subdirectory=src/integrations/prefect-redis repro.py
57
57
```
58
58
59
+
## inline script metadata
60
+
61
+
PEP 723 lets you embed dependencies directly in a script:
62
+
63
+
```python
64
+
# /// script
65
+
# dependencies = ["httpx", "rich"]
66
+
# requires-python = ">=3.12"
67
+
# ///
68
+
69
+
import httpx
70
+
from rich import print
71
+
72
+
print(httpx.get("https://httpbin.org/get").json())
73
+
```
74
+
75
+
run with `uv run script.py` - uv reads the metadata and creates an environment with those dependencies. no pyproject.toml, no requirements.txt, just a self-contained script.
76
+
77
+
this is how you share reproducible examples. put the dependencies in the file itself, and anyone with uv can run it.
78
+
59
79
## shareable one-liners
60
80
61
81
no file needed: