0%

typing:规范友好的代码

使用typing增强Python代码的可读性、可维护性,并帮助静态分析工具进行错误检查。

Python 的 typing 模块是用于 类型注解和静态类型检查 的标准库工具。它为你编写类型安全、可读性强的大型代码提供了支持。

基础类型和泛型容器类型

基础类型注解

1
int, float, str, bool, bytes
  • Python 内置类型就可以直接用作注解

泛型容器类型

类型 用法示例
List List[int] — 整数列表
Dict Dict[str, float] — 键为字符串,值为浮点数
Tuple Tuple[int, str] — 二元组
Set Set[int]
FrozenSet FrozenSet[str]
  • Python 3.9+ 支持原生写法,如 list[int]替代 List[int]
    例如:
1
2
3
4
5
6
from typing import List

def average(scores: List[float]) -> float:
return sum(scores) / len(scores)

print(average([90.0, 85.5, 78.0])) # 输出:84.5
1
2
3
4
5
6
7
from typing import Dict

def get_student_score(name: str, records: Dict[str, float]) -> float:
return records[name]

data = {"Alice": 91.5, "Bob": 88.0}
print(get_student_score("Alice", data)) # 输出:91.5
1
2
3
4
5
6
7
from typing import Tuple

def get_student_info() -> Tuple[str, int]:
return ("Alice", 20)

name, age = get_student_info()
print(name, age) # 输出:Alice 20
1
2
3
4
5
6
7
8
9
10
from typing import Dict, List

def total_scores(data: Dict[str, List[int]]) -> Dict[str, int]:
return {name: sum(scores) for name, scores in data.items()}

records = {
"Alice": [90, 85, 92],
"Bob": [78, 80, 74]
}
print(total_scores(records)) # 输出:{'Alice': 267, 'Bob': 232}

通用类型工具

工具 描述
Any 任意类型
Union 多种类型之一,例如 Union[int, str]
Optional 可以是某种类型或 None,等价于 Union[T, None]
Callable 函数类型,例如 Callable[[int, int], str] 表示接受两个 int 返回 str 的函数
Literal 指定字面值,例如 Literal[‘yes’, ‘no’]
TypeVar 泛型类型变量(定义泛型函数或类时使用)
Generic 创建自定义泛型类时用
Final 声明不能被重写或赋值的变量/属性
ClassVar 用于声明类变量,而非实例变量
例如:

TypeVar

1
2
3
4
5
6
7
8
9
from typing import TypeVar, Tuple

T = TypeVar('T') # 声明一个通用类型变量 T

def swap(a: T, b: T) -> Tuple[T, T]:
return b, a

print(swap(1, 2)) # 输出: (2, 1) —— T 被推断为 int
print(swap("hi", "bye")) # 输出: ('bye', 'hi') —— T 被推断为 str
  • T 可以是任何类型(int、str、float…)

Generic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from typing import TypeVar, Generic

T = TypeVar('T')

class Box(Generic[T]):
def __init__(self, content: T):
self.content = content

def get(self) -> T:
return self.content

int_box = Box
print(int_box.get()) # 输出: 123

str_box = Box[str]("hello")
print(str_box.get()) # 输出: hello
  • Box[T] 表示这是一个“存放任意类型 T 的盒子”
  • Generic[T] 表示类是“泛型类”

Any

1
2
3
4
5
6
7
8
from typing import Any

def print_anything(x: Any) -> None:
print(f"Received: {x}")

print_anything(42) # int
print_anything("hello") # str
print_anything([1, 2, 3]) # list
  • Any 表示函数接受任何类型的数据

结构化和协议

类型 描述
Protocol 定义接口或行为协议(Python 3.8+)
TypedDict 类似 dict 的结构化类型注解
NamedTuple 类型注解版本的命名元组
dataclass 与 @dataclass 一起使用可以加注解
例如:

TypedDict

1
2
3
4
5
6
7
8
9
10
11
12
from typing import TypedDict

class User(TypedDict):
id: int
name: str
is_active: bool

def greet(user: User) -> str:
return f"Hello, {user['name']}!"

u = {"id": 1, "name": "Alice", "is_active": True}
print(greet(u)) # 输出: Hello, Alice!
  • 明确了 user 必须包含哪些字段
  • 静态检查工具(如 mypy)可以发现拼写错误或字段缺失

Protocol

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from typing import Protocol

class Reader(Protocol):
def read(self) -> str:
...

class FileReader:
def read(self) -> str:
return "data from file"

class Dummy:
def read(self) -> str:
return "test data"

def load(r: Reader) -> str:
return r.read()

print(load(FileReader())) # 输出: data from file
print(load(Dummy())) # 输出: test data

当你只关心某个对象是否有某些方法或属性(而不是它的具体类型),使用 Protocol 可以定义“接口”,实现 Python 的“结构子类型检查”。

  • load() 不关心对象的真实类型,只要有 .read() 方法就行
  • 非侵入式,“只要像鸭子,它就能飞”(典型的 duck typing)

dataclasses

1
2
3
4
5
6
7
8
9
10
11
from dataclasses import dataclass

@dataclass
class User:
id: int
name: str
is_active: bool = True # 可以设置默认值

# 实例化
u = User(id=1, name="Alice")
print(u) # 输出: User(id=1, name='Alice', is_active=True)
  • 自动生成了 init() 方法
  • 自动实现了 repr() 和 eq() 等方法
1
2
3
4
5
6
7
8
9
10
11
from dataclasses import dataclass

@dataclass(order=True)
class Product:
price: float
name: str

p1 = Product(19.9, "A")
p2 = Product(29.9, "B")

print(p1 < p2) # True,按字段顺序比较(先比 price)
  • 支持比较大小
1
2
3
4
5
6
7
8
9
from dataclasses import dataclass, field

@dataclass
class Token:
value: str
secret: str = field(repr=False, compare=False) # 不显示、也不参与比较

t1 = Token("abc", "secret123")
print(t1) # 输出: Token(value='abc')

忽略某个字段:field(repr=False, compare=False, default=…)

特殊类型工具

类型 描述
NewType 定义新的类型别名,例如:UserId = NewType(‘UserId’, int)
Type 类对象的类型,例如 Type[BaseClass]
Self 指代自身(Python 3.11+)
例如:

NewType

创建“伪新类型”,用于静态类型区分

1
2
3
4
5
6
7
8
9
from typing import NewType

UserId = NewType('UserId', int)

def get_user_name(user_id: UserId) -> str:
return f"User#{user_id}"

get_user_name(UserId(123)) # ✅ 正确
# get_user_name(123) # ❌ mypy 会报错:需要 UserId,而不是 int

Type

Type[T] 接收一个类对象(而不是实例)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from typing import Type

class Animal:
def speak(self) -> str:
return "..."

class Dog(Animal):
def speak(self) -> str:
return "Woof!"

def make_animal(animal_cls: Type[Animal]) -> Animal:
return animal_cls()

a = make_animal(Dog) # ✅ 传类不是传对象
print(a.speak()) # Woof!
  • Type[Animal] 表示“Animal 的子类”,不是实例。

Self(Python 3.11+)

用于方法返回当前类类型

1
2
3
4
5
6
7
8
9
10
11
12
from typing import Self

class Builder:
def set_name(self, name: str) -> Self:
self.name = name
return self

def set_age(self, age: int) -> Self:
self.age = age
return self

b = Builder().set_name("Alice").set_age(30)
  • Self 解决了链式调用返回类型难以表达的问题。
  • 自动适配子类返回自身,无需硬编码类名。