@@ -6,20 +6,20 @@ title = 'Python Generics'
66本篇文件介绍 Python 中的 泛型(Generics)
77
88### Intro
9- 在没有泛型的情况下, 会遇上一下几个问题 :
9+ 在没有泛型的情况下, 会遇上以下几个问题 :
1010
11- 1 . 难以表达意图
11+ 1 . 难以表达意图
1212 假设你编写了一个函数, 它接受一个列表, 并返回列表中的第一个元素.
1313 在不使用类型提示的情况下, 这个函数可以处理任何类型的列表, 但我们无法在函数签名中表达"返回的元素的类型与列表中的元素类型相同"这个意图
14- ``` Python
14+ ``` python
1515 def get_first_element (items ):
1616 return items[0 ]
1717 ```
1818
19- 2 . 丧失类型信息
19+ 2 . 丧失类型信息
2020 如果使用类型提示, 可能会像下面这样写, 但这样会丢失类型信息.
21- `list[Any]` 表示可以接收任何类型的列表, 但 `-> Any` 意味着不知道返回的元素类型是什么, 这使得 mypy 这里静态类型检测工具无法追踪类型 , 降低了代码的可读性和安全性
22- ```Python
21+ `list[Any]` 表示可以接收任何类型的列表, 但 `-> Any` 意味着不知道返回的元素类型是什么, 这使得 mypy 等静态类型检测工具无法追踪类型 , 降低了代码的可读性和安全性
22+ ```python
2323 from typing import Any
2424 def get_first_element (items : list[Any]) -> Any:
2525 return items[0 ]
@@ -28,38 +28,41 @@ title = 'Python Generics'
2828 first_str = get_first_element([" hello" , " world" ])
2929 ```
3030
31- 3 . 代码重复
31+ 3 . 代码重复
3232 如果为每种可能的类型都编写一个单独的函数, 则会导致代码重复
33- ```Python
34- def get_first_int (items : List [int ]) -> int :
33+ ```python
34+ def get_first_int (items : list [int ]) -> int :
3535 return items[0 ]
3636
37- def get_first_str (items : List [str ]) -> str :
37+ def get_first_str (items : list [str ]) -> str :
3838 return items[0 ]
3939 ```
4040
4141通过引入 ** 类型变量** (TypeVar) 来解决问题, 类型变量就像一个占位符, 代表在未来某时刻会被具体指定的类型
42- ```Python
42+ ```python
4343from typing import TypeVar
4444
4545T = TypeVar(" T" )
4646
4747def get_first_element (items : list[T]) -> T:
48- reuturn items[0 ]
48+ return items[0 ]
4949
5050# 现在, 类型检查工具可以正确推断出类型
5151first_str: str = get_first_element([" hello" , " world" ])
5252first_int: int = get_first_element([1 , 2 , 3 ])
5353```
54- - ` T = TypeVar('T') ` 定义了一个名为 T 的类型变量, 这里 T 只是一个约定熟成的名字, 也可以使用其他字母
54+
55+ - ` T = TypeVar('T') ` 定义了一个名为 T 的类型变量, 这里 T 只是一个约定俗成的名字, 也可以使用其他字母
5556- ` items: list[T] ` 表示 ` items ` 是一个列表, 其内部元素类型是 T
5657- ` -> T ` : 返回类型也是 T
5758- 当使用 ` ["hello", "world"] ` 调用函数时, 静态类型检查器会推断出 T 是 str, 返回类型为 str
5859- 当使用 ` [1, 2, 3] ` 调用函数时, T 被推断为 int
5960
61+ ** 注意** : 这个函数假设列表非空, 如果传入空列表会抛出 ` IndexError `
62+
6063### Generic Class
6164除了函数, 泛型也常用于定义泛型类
62- ``` Python
65+ ``` python
6366from typing import TypeVar, Generic
6467
6568T = TypeVar(" T" )
@@ -76,97 +79,138 @@ class Box(Generic[T]):
7679
7780# 创建一个存储字符串的 Box
7881string_box = Box([" apple" , " banana" ])
79- item_str = string_box.get() # str
82+ item_str = string_box.get() # str
8083string_box.add(" cherry" )
8184
8285# 创建一个存储整数的 Box
8386int_box = Box([10 , 20 ])
84- item_int = int_box.get() # int
87+ item_int = int_box.get() # int
8588int_box.add(30 )
8689```
87- - ` TypeVar ` 定义类型参数 : 相当于一个占位符, 将来由使用者指定具体类型
90+ - ` TypeVar ` 定义类型变量 : 相当于一个占位符, 将来由使用者指定具体类型
8891- ` Generic ` 定义泛型类或泛型接口: 使这个类在类型检查器眼中变成一个模板
8992
90-
91- ### Advanced Useage
93+ ### Advanced Usage
9294简单介绍一下泛型的一些进阶用法
9395
94- - 多类型参数
95- ``` Python
96+ - ** 多类型参数**
97+ ``` python
98+ from typing import TypeVar, Generic
99+
96100 K = TypeVar(" K" )
97101 V = TypeVar(" V" )
98102
99103 class Pair (Generic[K, V]):
100104 def __init__ (self , key : K, value : V):
101105 self .key = key
102106 self .value = value
107+
108+ def get_key (self ) -> K:
109+ return self .key
110+
111+ def get_value (self ) -> V:
112+ return self .value
113+
114+ # 使用示例
115+ pair = Pair(" name" , 25 ) # Pair[str, int]
103116 ```
104- 支持多个类型参数 , 类似 `dict[K, V]` 的结构
117+ 支持多个类型变量 , 类似 `dict[K, V]` 的结构
105118
106- - Constraints 类型约束
107- 有时候可能希望泛型只能是某些类型
108- ```Python
119+ - ** 类型约束 ( Constraints) **
120+ 有时候可能希望泛型只能是某些特定类型
121+ ```python
109122 from typing import TypeVar
110123
111124 Number = TypeVar(' Number' , int , float )
112125
113126 def add (a : Number, b : Number) -> Number:
114127 return a + b
128+
129+ # 正确使用
130+ result1 = add(1 , 2 ) # int
131+ result2 = add(1.5 , 2.3 ) # float
132+
133+ # 错误使用: mypy 会报错
134+ # result3 = add("hello", "world") # str 不被允许
115135 ```
116136 `Number` 只能为 int 或 float , 传入其他类型, 类型检查工具会报错
117137
118- - Convariant / Contravariant 协变于逆变
119- 在泛型类型中, 可以控制类型参数的变型关系
120- ```Python
138+ - ** 协变与逆变 (Covariance / Contravariance) **
139+ 在泛型类型中, 可以控制类型变量的变型关系
140+ ```python
121141 from typing import Generic, TypeVar
122142
123- T_co = TypeVar(" T_co" , convariant = True ) # 协变
124- T_contra = TypeVar(" T_contra" , contravariant = True ) # 逆变
143+ T_co = TypeVar(" T_co" , covariant = True ) # 协变
144+ T_contra = TypeVar(" T_contra" , contravariant = True ) # 逆变
125145
126- class ReadOnlyBox (Generic[T_co]):
146+ class Producer (Generic[T_co]):
147+ """ 只产出 T_co 类型的数据 (协变)"""
127148 def __init__ (self , value : T_co):
128- self .value = value
129-
130- class Writer (Generic[T_contra]):
131- def __init__ (self , value , T_contra ):
132- ...
149+ self ._value = value
150+
151+ def get (self ) -> T_co:
152+ return self ._value
153+
154+ class Consumer (Generic[T_contra]):
155+ """ 只消费 T_contra 类型的数据 (逆变)"""
156+ def __init__ (self ):
157+ pass
158+
159+ def consume (self , value : T_contra) -> None :
160+ print (f " Consuming: { value} " )
133161 ```
134- 协变: 面向产出(只读), 允许子类替代父类
135- 逆变: 面向消费(只写), 允许父类替代子类
136- 主要用于接口设计中(读 / 写分离)
162+ - ** 协变 (covariant) ** : 如果 A 是 B 的子类型, 那么 `Generic[A]` 也是 `Generic[B]` 的子类型. 适用于只产出数据的场景
163+ - ** 逆变 (contravariant) ** : 如果 A 是 B 的子类型, 那么 `Generic[B]` 是 `Generic[A]` 的子类型. 适用于只消费数据的场景
164+ - 这主要用于接口设计中的读 / 写分离
137165
138- - 泛型与 Protocol
166+ - ** 泛型与 Protocol**
139167 `Protocol` 允许定义泛型接口 (duck typing)
140- ```Python
141- from typing import Protocol
168+ ```python
169+ from typing import Protocol, TypeVar
170+
171+ T = TypeVar(' T' )
142172
143173 class SupportsLen (Protocol ):
144174 def __len__ (self ) -> int : ...
145175
146176 def total_length (items : list[SupportsLen]) -> int :
147177 return sum (len (x) for x in items)
178+
179+ # 使用示例
180+ result = total_length([" hello" , [1 , 2 , 3 ], {" a" : 1 }]) # 可以接受任何有 __len__ 方法的对象
148181 ```
149- 任何实现了`__len__ ` 方法的对象都能接受 , 比继承更加灵活
182+ 任何实现了`__len__ ` 方法的对象都能被接受 , 比继承更加灵活
150183
151- - 泛型在标准库中的使用
152- - 集和类 : `list[T]` , `dict[K, V]` , `set[T]`
184+ - ** 泛型在标准库中的使用**
185+ - 集合类 : `list[T]` , `dict[K, V]` , `set[T]`
153186 - 迭代器: `Iterator[T]` , `Iterable[T]`
154187 - 函数工具: `Callable[[T1, T2], R]`
155188 - 上下文管理器: `ContextManager[T]`
156189
157- ```Python
190+ ```python
158191 from typing import Callable
159192
160- F = Callable[[int , int ], int ]
161-
162- def operate (a : int , b : int , func : F) -> int :
193+ def operate (a : int , b : int , func : Callable[[int , int ], int ]) -> int :
163194 return func(a, b)
195+
196+ # 使用示例
197+ def add (x : int , y : int ) -> int :
198+ return x + y
199+
200+ def multiply (x : int , y : int ) -> int :
201+ return x * y
202+
203+ result1 = operate(5 , 3 , add) # 8
204+ result2 = operate(5 , 3 , multiply) # 15
164205 ```
165206
166-
167207# ## Wrapping Up
168208泛型是 Python 类型提示系统中一个非常强大的工具, 它通过类型变量帮助我们编写更加灵活、安全且可维护的代码.
169209
170- 它虽然不会影响程序的运行时行为, 但它为静态类型分析提供了必要的信息, 使得代码意图更加清晰, 并且能在早期发现类型错误.
210+ 它虽然不会影响程序的运行时行为 (类型信息在运行时会被擦除) , 但它为静态类型分析提供了必要的信息, 使得代码意图更加清晰, 并且能在早期发现类型错误.
171211
172- Python 的泛型是类型提示系统的一部分, 和 C++ / Java 的编译期泛型不同, 它的作用主要是: 帮助 IDE 和类型检查工具发现类型错误, 提升代码可读性和可维护性, 提供更精确的 API 类型签名等
212+ Python 的泛型是类型提示系统的一部分, 和 C++ / Java 的编译期泛型不同, 它的作用主要是:
213+ - 帮助 IDE 和类型检查工具发现类型错误
214+ - 提升代码可读性和可维护性
215+ - 提供更精确的 API 类型签名
216+ - 支持更好的代码重用和抽象
0 commit comments