typing_extensions
是一个 Python 库,提供对 Python 类型注解的扩展支持,包含在较新 Python 版本中引入的类型功能(如 Literal
、TypedDict
、Protocol
),并将其回溯到旧版本。它是 typing
标准库的补充,广泛用于需要高级类型注解的场景,如静态类型检查(使用 mypy
、pyright
)、IDE 类型提示和现代 Python 项目。
以下是对 typing_extensions
库的详细介绍,包括其功能、用法和实际应用,基于最新信息(截至 2025)。
Literal
、TypedDict
、Protocol
、Annotated
等。mypy
、pyright
等工具的类型验证能力。Self
、TypeGuard
)。pip install typing-extensions
import typing_extensions
print(typing_extensions.__version__) # 示例输出: 4.12.2
注意:
typing
模块已包含许多功能,typing_extensions
主要用于兼容旧版本或实验性功能。TypeAliasType
)需 Python 3.12+。typing_extensions
扩展了 typing
模块,提供了多种高级类型和工具。以下是主要功能和示例。
表示变量只能取特定字面值。
from typing_extensions import Literal
def set_mode(mode: Literal["on", "off"]) -> None:
print(f"Mode set to {mode}")
set_mode("on") # 正确
# set_mode("standby") # mypy 报错: Argument 1 to "set_mode" has incompatible type "str"; expected "Literal['on', 'off']"
说明:
Literal
限制值为指定字面量(如字符串、整数)。typing
),typing_extensions
回溯到 3.5+。定义字典的键值类型,支持必需/可选字段。
from typing_extensions import TypedDict, Required, NotRequired
class Movie(TypedDict):
title: str
year: NotRequired[int] # 可选字段
rating: Required[float] # 必需字段
movie: Movie = {"title": "Inception", "rating": 8.8}
# movie = {"title": "Inception"} # mypy 报错: Missing key "rating"
说明:
TypedDict
指定字典结构,增强类型安全。Required
/NotRequired
(PEP 728,Python 3.11+)支持显式字段约束。定义结构化类型(鸭子类型),支持隐式接口。
from typing_extensions import Protocol
class HasName(Protocol):
def get_name(self) -> str:
...
class Person:
def get_name(self) -> str:
return "Alice"
class Robot:
def get_name(self) -> str:
return "R2-D2"
def greet(entity: HasName) -> None:
print(f"Hello, {entity.get_name()}!")
greet(Person()) # 正确
greet(Robot()) # 正确
说明:
Protocol
定义接口,任何实现指定方法的类都符合类型。typing
),回溯到 3.5+。附加元数据到类型注解。
from typing_extensions import Annotated
def process_data(data: Annotated[str, "Sensitive"]) -> None:
print(f"Processing: {data}")
process_data("secret")
说明:
Annotated
为类型添加元数据,供工具或运行时使用。typing
),回溯到 3.5+.表示当前类的类型,适合方法返回自身实例。
from typing_extensions import Self
class Node:
def __init__(self, value: int) -> None:
self.value = value
self.next: Node | None = None
def add_next(self, value: int) -> Self:
self.next = Node(value)
return self
node = Node(1).add_next(2)
print(node.next.value) # 输出: 2
说明:
Self
避免显式类名,提升代码可维护性。typing
),回溯到 3.5+.缩小类型范围,优化条件分支。
from typing_extensions import TypeGuard
def is_string_list(value: list) -> TypeGuard[list[str]]:
return all(isinstance(x, str) for x in value)
def process_list(data: list) -> None:
if is_string_list(data):
print(data[0].upper()) # mypy 知道 data 是 list[str]
else:
print("Not a string list")
process_list(["hello", "world"]) # 输出: HELLO
说明:
TypeGuard
帮助类型检查器推断条件分支中的类型。typing
),回溯到 3.5+.标记方法重写或废弃(实验性)。
from typing_extensions import override, deprecated
class Base:
def process(self) -> None:
print("Base process")
class Derived(Base):
@override
def process(self) -> None:
print("Derived process")
@deprecated("Use new_function instead")
def old_function() -> None:
print("Old function")
Derived().process() # 输出: Derived process
old_function() # mypy 警告: Call to deprecated function
说明:
override
:确保方法正确重写父类方法(PEP 698)。deprecated
:标记废弃功能(PEP 702)。typing_extensions
和类型检查器支持。定义类型别名(Python 3.12+)。
from typing_extensions import TypeAliasType
MyList = TypeAliasType("MyList", list[int])
x: MyList = [1, 2, 3]
# x = [1, "2"] # mypy 报错: Incompatible type
说明:
TypeAliasType
提供显式类型别名声明。typing_extensions
提供实验性支持。mypy
、pyright
、pylance
等工具无缝协作。TypeAliasType
)仅限较新 Python 版本。typing
对比:
typing
:标准库,功能随 Python 版本更新。typing_extensions
:提供提前访问和回溯支持,适合前沿项目。TypedDict
和 Literal
定义 API 结构。Protocol
定义接口,确保鸭子类型安全。Annotated
添加元数据。Self
和 TypeGuard
增强类型推断。示例(FastAPI 集成):
from fastapi import FastAPI
from typing_extensions import TypedDict, Literal
from pydantic import BaseModel
from loguru import logger
app = FastAPI()
# 定义 TypedDict
class User(TypedDict):
id: int
role: Literal["admin", "user"]
# Pydantic 模型
class UserResponse(BaseModel):
id: int
role: Literal["admin", "user"]
# 模拟数据库
users: list[User] = [{"id": 1, "role": "admin"}, {"id": 2, "role": "user"}]
@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int):
for user in users:
if user["id"] == user_id:
logger.info(f"Found user: {user}")
return user
logger.error(f"User {user_id} not found")
raise HTTPException(status_code=404, detail="User not found")
说明:
TypedDict
定义用户数据结构。Literal
限制 role
值为 "admin"
或 "user"
。Pydantic
确保 API 响应类型安全。loguru
记录日志。pip install mypy
mypy script.py
typing_extensions
最新版本,以支持 override
等功能。npm install -g pyright
pyright script.py
typing_extensions
的实验性功能。# pyproject.toml
[tool.mypy]
plugins = ["typing_extensions"]
strict = true
typing
或需 typing_extensions
。Literal
在 Python 3.8+ 的 typing
中可用,无需 typing_extensions
。override
)需最新 mypy
(0.990+)或 pyright
。typing_extensions
仅影响静态检查。mypy
检查时间。typing
模块:标准库,适合基本类型注解。pydantic
/dataclasses
:运行时验证,结合 typing_extensions
增强类型。typeshed
:提供标准库类型定义,可结合使用。以下是一个综合示例,结合 typing_extensions
、FastAPI
和 loguru
,展示多种类型注解:
from fastapi import FastAPI, Depends
from typing_extensions import TypedDict, Literal, Protocol, Self, TypeGuard, override
from pydantic import BaseModel
from loguru import logger
import httpx
# 配置日志
logger.add("app.log", rotation="1 MB", level="INFO")
# Protocol 定义接口
class ApiClient(Protocol):
def fetch_data(self, endpoint: str) -> dict:
...
# TypedDict 定义数据结构
class UserData(TypedDict):
id: int
role: Literal["admin", "user"]
# Pydantic 模型
class UserResponse(BaseModel):
id: int
role: Literal["admin", "user"]
# 实现 ApiClient
class HttpClient:
def __init__(self, base_url: str) -> None:
self.client = httpx.AsyncClient(base_url=base_url)
@override
async def fetch_data(self, endpoint: str) -> dict:
response = await self.client.get(endpoint)
response.raise_for_status()
return response.json()
# 类型检查函数
def is_user_data(data: dict) -> TypeGuard[UserData]:
return "id" in data and "role" in data and data["role"] in ("admin", "user")
# FastAPI 应用
app = FastAPI()
async def get_client() -> ApiClient:
return HttpClient("https://api.example.com")
@app.get("/users/{user_id}", response_model=UserResponse)
async def get_user(user_id: int, client: ApiClient = Depends(get_client)):
try:
data = await client.fetch_data(f"/users/{user_id}")
if is_user_data(data):
logger.info(f"User data: {data}")
return data
logger.error(f"Invalid user data: {data}")
raise HTTPException(status_code=422, detail="Invalid user data")
except httpx.HTTPStatusError as e:
logger.error(f"API error: {e}")
raise HTTPException(status_code=e.response.status_code, detail=str(e))
说明:
Protocol
定义 API 客户端接口,HttpClient
实现接口。TypedDict
和 Literal
确保用户数据结构。TypeGuard
验证运行时类型。override
标记方法重写。FastAPI
和 httpx
,实现异步 API 调用。运行:
uvicorn main:app --reload
类型检查:
mypy main.py
typing
标签):https://stackoverflow.com/questions/tagged/typing