|
| 1 | +--- |
| 2 | +created: "2025-10-25" |
| 3 | +updated: "2025-10-25" |
| 4 | +--- |
| 5 | +> *最讨厌 python 的动态类型和无声明。 |
| 6 | +> |
| 7 | +> ——鸽子 2025/10/25* |
| 8 | +
|
| 9 | +[typing --- 对类型提示的支持 — Python 3.14.0 文档](https://docs.python.org/zh-cn/3/library/typing.html#) |
| 10 | + |
| 11 | +所以要写多多标记变量 `type`,不然在大项目不知道类型就要吃亏。 |
| 12 | +- 没有类型提示 |
| 13 | +- 写错也没提示 |
| 14 | +- 看不懂别人代码 |
| 15 | + |
| 16 | +需要知道的是 `typing hinting` 本质上还是 **标注**,所以并不是真的会强制进行类型检查,比如说下面的代码实际上可以运行。 |
| 17 | + |
| 18 | +```python |
| 19 | + |
| 20 | +def get_text(): |
| 21 | + a: str = 1 # 虽然标记为 str,但是实际上是 int |
| 22 | + return a |
| 23 | + |
| 24 | +string = get_text() # 调用代码的时候 IDE 类型推导 string 也应该是 str 类型,然鹅实际上是 int |
| 25 | +``` |
| 26 | + |
| 27 | +乱标记后面报错就老实了。 |
| 28 | + |
| 29 | +> [!WARNING] |
| 30 | +> 本文大写的类型标注都来自于 `typing` 模块。小写的类型是已经内置的类型。 |
| 31 | +> |
| 32 | +> 此外 `T` 代表可以用其他类型替代。 |
| 33 | +
|
| 34 | +## No Pain |
| 35 | + |
| 36 | +常见的基本类型就 `float` `int` `str` 之类这种。`class` 定义的类型也可以是类型的一种。 |
| 37 | + |
| 38 | +```python |
| 39 | +a: str = 'abs' |
| 40 | + |
| 41 | +def sum(a: int, b: int) -> int: |
| 42 | + return a + b; |
| 43 | +``` |
| 44 | + |
| 45 | +## Mild |
| 46 | + |
| 47 | +稍微复杂点的是 `collections` 集合类型。比如说 `dict` `list` 等。 |
| 48 | + |
| 49 | +一般可以用 `[]` 来进行具体的类型指定,比如说 `dict[str, int]` 代表 key 为 str 类型,value 为 int 类型的字典。 *可以类比 C++ 的 <>* |
| 50 | + |
| 51 | +```cpp |
| 52 | +a: list[str] = [1,2,3] |
| 53 | +b: dict[str, float] = {"apple": 0.75, "banana": 1.25} |
| 54 | +c: tuple[int, ...] = (1, 2, 3) // ... 意思就是后面的和前面的一样 tuple[int, int, int] |
| 55 | +d: set[int] = {101, 102, 103} |
| 56 | +``` |
| 57 | +
|
| 58 | +## Moderate |
| 59 | +
|
| 60 | +### 集合类型 |
| 61 | +
|
| 62 | +typing 模块有很多很好用的类型。 |
| 63 | +
|
| 64 | +- `Option[T]`: 要么是 T 类型,要么是 None |
| 65 | +- `Union[T1, T2, ...]`: 类型是 `T1, T2, ...` 其中一个。上面的 `Option[T]` 等同 `Union[T, None]` |
| 66 | +- `Any`:任何类型,相当于不标注类型。(慎用) |
| 67 | +- `Callable[[T1, T2, ...], T3]`: 可调用的对象(例如函数),其中 `T1, T2, ...` 是参数列表,`T3` 是返回类型。 |
| 68 | +- `Literal[v1, v2, ...]`: 只会是其中一个字面值。 |
| 69 | +
|
| 70 | +### 类型别名 |
| 71 | +
|
| 72 | +用 `type` 标注类型别名。 |
| 73 | +
|
| 74 | +```python |
| 75 | +type Vector = list[float] |
| 76 | +
|
| 77 | +def scale(scalar: float, vector: Vector) -> Vector: |
| 78 | + return [scalar * num for num in vector] |
| 79 | +
|
| 80 | +# 通过类型检查;浮点数列表是合格的 Vector。 |
| 81 | +new_vector = scale(2.0, [1.0, -4.2, 5.4]) |
| 82 | +``` |
| 83 | + |
| 84 | +`type` 语句是 3.12 加的,如果要向下兼容可以用 `Vector = list[float]` 直接赋值。 |
| 85 | + |
| 86 | +### NewType |
| 87 | + |
| 88 | +可以用 NewType 创建与原类型不同的类型。 |
| 89 | + |
| 90 | +```python |
| 91 | +from typing import NewType |
| 92 | + |
| 93 | +UserId = NewType('UserId', int) |
| 94 | +some_id = UserId(524313) |
| 95 | +``` |
| 96 | + |
| 97 | +```python |
| 98 | +def get_user_name(user_id: UserId) -> str: |
| 99 | + ... |
| 100 | + |
| 101 | +# 通过类型检查 |
| 102 | +user_a = get_user_name(UserId(42351)) |
| 103 | + |
| 104 | +# 未通过类型检查;整数不能作为 UserId |
| 105 | +user_b = get_user_name(-1) |
| 106 | +``` |
| 107 | + |
| 108 | +### TYPE_CHECKING |
| 109 | + |
| 110 | +一个在类型检查时会被检查器当成 `True` |
| 111 | + |
| 112 | +### 类对象类型 |
| 113 | + |
| 114 | +`type[T]` 表示 class T 的类型。(类型本身,而非实例) |
| 115 | + |
| 116 | +## Severe |
| 117 | + |
| 118 | +### 懒加载 |
| 119 | + |
| 120 | +在遇到定义同时需要类型注解的时候会产生先有鸡还是先有蛋的问题。 |
| 121 | + |
| 122 | +为此,使用 `from __future__ import annotations` 解决。在使用后,类型检查会推迟到文档定义完才进行。 |
| 123 | + |
| 124 | +### 为了类型提示而导致循环引用 |
| 125 | + |
| 126 | +本身只想导入模块来获取类型提示,但是两个模块互相需要类型的话,就会产生循环引用。 |
| 127 | + |
| 128 | +为此,可以用下面这个小巧思。 |
| 129 | + |
| 130 | +```python |
| 131 | +from __future__ import annotaions |
| 132 | +from typing import TYPE_CHECKING |
| 133 | + |
| 134 | +if TYPE_CHECKING: |
| 135 | + from module_a import ClassA |
| 136 | +``` |
| 137 | + |
| 138 | +这样在实际运行时不会导入模块,而在类型检查时检查器会进行类型导入。 |
| 139 | + |
| 140 | +## Very Severe |
| 141 | + |
| 142 | +### Generic 泛型 |
| 143 | + |
| 144 | +懒得写。 |
| 145 | + |
| 146 | +```python |
| 147 | +from __future__ import annotations |
| 148 | + |
| 149 | +from collections.abc import Mapping |
| 150 | +from typing import Iterable, Protocol, TypeVar |
| 151 | + |
| 152 | +T = TypeVar('T') |
| 153 | + |
| 154 | + |
| 155 | +class Addable[T](Protocol): |
| 156 | + def __add__(self: T, other: T) -> T: ... |
| 157 | + |
| 158 | + |
| 159 | +def sum[T: Addable](a: T, b: T) -> T: |
| 160 | + return a + b |
| 161 | + |
| 162 | + |
| 163 | +def print_iter(iterable: Iterable) -> None: |
| 164 | + for item in iterable: |
| 165 | + print(item) |
| 166 | + |
| 167 | + |
| 168 | +def print_map(mapping: Mapping) -> None: |
| 169 | + for key, value in mapping.items(): |
| 170 | + print(key, value) |
| 171 | +``` |
| 172 | + |
| 173 | +## Worst Pain Possible |
| 174 | + |
| 175 | +pass |
0 commit comments