在这里插入图片描述

  【个人主页:玄同765

大语言模型(LLM)开发工程师中国传媒大学·数字媒体技术(智能交互与游戏设计)

深耕领域:大语言模型开发 / RAG知识库 / AI Agent落地 / 模型微调

技术栈:Python / LangChain/RAG(Dify+Redis+Milvus)| SQL/NumPy | FastAPI+Docker ️

工程能力:专注模型工程化部署、知识库构建与优化,擅长全流程解决方案 

      

专栏传送门:LLM大模型开发 项目实战指南Python 从真零基础到纯文本 LLM 全栈实战​​​​​从零学 SQL + 大模型应用落地大模型开发小白专属:从 0 入门 Linux&Shell

     

「让AI交互更智能,让技术落地更高效」

欢迎技术探讨/项目合作! 关注我,解锁大模型与智能交互的无限可能!

你是不是用 FastAPI 写接口时只会用 str、int、list 这些基础类型注解?或者在写 LangChain 工具链时,不知道怎么让大模型准确识别工具参数的类型?这篇详解 Python typing 模块的常用类型、类型别名、泛型、协议、类型守卫,配合 FastAPI 接口开发和 LLM 工具调用的代码示例,让你彻底搞懂类型注解的底层逻辑和应用场景,代码可读性提升一倍,调试时间减少一半。


一、引言:为什么要学 Python typing 模块?

在传统的 Python 开发中,很多开发者认为类型注解是 “多余的负担”,因为 Python 是动态类型语言,不需要在代码中明确变量和函数的类型。但随着项目规模的扩大和团队协作的深入,类型注解的重要性逐渐显现出来:

  1. 提高代码可读性:类型注解可以让其他开发者快速理解变量和函数的用途、参数和返回值的类型;
  2. 减少调试时间:很多 IDE(如 PyCharm、VS Code)会根据类型注解提供代码提示和类型检查功能,提前发现潜在的类型错误;
  3. 提升开发效率:类型注解可以让自动化工具(如 FastAPI 的 OpenAPI 文档生成器、LangChain 的工具链参数识别器)更好地理解代码,从而减少手动配置的时间;
  4. 降低维护成本:类型注解可以让代码的逻辑更清晰,修改代码时更不容易引入新的错误。

在 FastAPI 开发和 LLM 应用开发中,类型注解更是不可或缺的核心技术:

  • FastAPI 开发:FastAPI 基于类型注解实现数据校验和 OpenAPI 文档生成,没有类型注解,FastAPI 的自动校验和文档生成功能将无法使用;
  • LLM 应用开发:LangChain、FastGPT 等 LLM 应用框架会根据类型注解识别工具参数的类型和格式,从而让大模型更准确地调用工具。

二、Python typing 模块的发展历程

Python typing 模块的发展历程可以分为以下几个阶段:

  1. Python 3.0 之前:Python 没有类型注解功能,只能通过文档字符串和代码注释说明变量和函数的类型;
  2. Python 3.0-3.5:Python 3.0 引入了函数注解(Function Annotations),允许开发者在函数定义中为参数和返回值添加任意表达式作为注解,但这些注解不会被 Python 解释器检查;
  3. Python 3.5:Python 3.5 引入了 typing 模块,提供了丰富的类型注解工具,如 List、Dict、Tuple、Optional、Union 等,同时引入了类型提示(Type Hints)的概念;
  4. Python 3.8+:Python 3.8 引入了 typing.Literal、 typing.TypedDict 等类型注解工具,Python 3.9 引入了内置类型的泛型支持(如 list [int]、dict [str, float]),Python 3.10 引入了类型守卫(Type Guard)和联合类型的新写法(如 int | str)。

三、Python typing 模块的常用类型注解

Python typing 模块提供了丰富的类型注解工具,覆盖了 API 接口开发和 LLM 应用开发中常见的数据类型。

3.1 基础类型注解
类型注解 功能 示例
int 整数类型 age: int = 18
float 浮点数类型 score: float = 95.5
str 字符串类型 username: str = "zhangsan"
bool 布尔类型 is_active: bool = True
None 空类型 description: None = None

示例代码(FastAPI 接口开发)

from fastapi import FastAPI

app = FastAPI()

@app.get("/user/{user_id}")
def read_user(user_id: int, name: str = None, is_active: bool = True):
    """
    获取单个用户信息的接口
    :param user_id: 用户的唯一标识符(整数类型,必填)
    :param name: 用户的姓名(字符串类型,可选,默认值为None)
    :param is_active: 用户是否激活(布尔类型,可选,默认值为True)
    :return: 用户信息的JSON响应
    """
    return {"user_id": user_id, "name": name, "is_active": is_active}

示例代码(LLM 应用开发)

from langchain.agents import tool

@tool
def add_numbers(num1: int, num2: int) -> int:
    """
    计算两个整数的和
    :param num1: 第一个整数
    :param num2: 第二个整数
    :return: 两个整数的和
    """
    return num1 + num2
3.2 复合类型注解
类型注解 功能 示例
List[T] 或 list[T](Python 3.9+) 列表类型,元素类型为 T tags: List[str] = ["技术", "编程"]
Dict[K, V] 或 dict[K, V](Python 3.9+) 字典类型,键类型为 K,值类型为 V user_profile: Dict[str, str] = {"name": "zhangsan", "email": "zhangsan@example.com"}
Tuple[T1, T2, ..., Tn] 或 tuple[T1, T2, ..., Tn](Python 3.9+) 元组类型,元素类型为 T1 到 Tn,长度固定 coordinates: Tuple[float, float] = (116.403874, 39.915168)
Set[T] 或 set[T](Python 3.9+) 集合类型,元素类型为 T,无重复 unique_tags: Set[str] = {"技术", "编程", "技术"}

示例代码(FastAPI 接口开发)

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List, Dict, Tuple

app = FastAPI()

class User(BaseModel):
    user_id: int
    username: str
    email: str
    age: int = None
    tags: List[str] = []
    user_profile: Dict[str, str] = {}
    coordinates: Tuple[float, float] = None

@app.post("/register")
def user_register(user: User):
    """
    用户注册的接口
    :param user: 用户注册信息(包含复合类型字段)
    :return: 注册成功的响应
    """
    return {"message": "用户注册成功", "data": user}

示例代码(LLM 应用开发)

from langchain.agents import tool
from typing import List, Dict

@tool
def filter_users(users: List[Dict[str, str]], min_age: int) -> List[Dict[str, str]]:
    """
    根据年龄过滤用户列表
    :param users: 用户列表(包含姓名、年龄、邮箱字段)
    :param min_age: 最小年龄
    :return: 年龄大于等于min_age的用户列表
    """
    filtered_users = [user for user in users if int(user["age"]) >= min_age]
    return filtered_users
3.3 可选类型和联合类型
类型注解 功能 示例
Optional[T] 或 `T None`(Python 3.10+) 可选类型,值可以是 T 或 None description: Optional[str] = None
Union[T1, T2, ..., Tn] 或 `T1 T2 ... Tn`(Python 3.10+) 联合类型,值可以是 T1 到 Tn 中的任意类型 avatar: Union[str, bytes] = None

示例代码(FastAPI 接口开发)

from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional, Union

app = FastAPI()

class User(BaseModel):
    user_id: int
    username: str
    email: str
    age: Optional[int] = None
    avatar: Union[str, bytes] = None

@app.post("/update-user")
def update_user(user: User):
    """
    更新用户信息的接口
    :param user: 用户更新信息(包含可选类型和联合类型字段)
    :return: 更新成功的响应
    """
    return {"message": "用户信息更新成功", "data": user}

示例代码(LLM 应用开发)

from langchain.agents import tool
from typing import Optional, Union

@tool
def calculate_discount(price: float, discount: Union[float, int], user_level: Optional[int] = None) -> float:
    """
    计算商品的折扣价格
    :param price: 商品原价(浮点数类型)
    :param discount: 折扣(浮点数类型表示折扣率,整数类型表示折扣金额)
    :param user_level: 用户等级(可选类型,用于额外折扣)
    :return: 折扣后的价格
    """
    if isinstance(discount, float):
        discounted_price = price * (1 - discount)
    elif isinstance(discount, int):
        discounted_price = price - discount
    else:
        raise ValueError("折扣必须是浮点数或整数类型")
    
    if user_level and user_level >= 3:
        discounted_price *= 0.95  # 三级及以上用户额外享受5%的折扣
    
    return round(discounted_price, 2)
3.4 字面量类型
类型注解 功能 示例
Literal[T1, T2, ..., Tn] 或 `T1 T2 ... Tn`(Python 3.10+) 字面量类型,值必须是 T1 到 Tn 中的任意一个字面量 order_status: Literal["pending", "shipped", "delivered"] = "pending"

示例代码(FastAPI 接口开发)

from fastapi import FastAPI
from pydantic import BaseModel
from typing import Literal

app = FastAPI()

class Order(BaseModel):
    order_id: int
    product_name: str
    price: float
    quantity: int
    order_status: Literal["pending", "shipped", "delivered"] = "pending"

@app.post("/create-order")
def create_order(order: Order):
    """
    创建订单的接口
    :param order: 订单信息(包含字面量类型字段)
    :return: 订单创建成功的响应
    """
    return {"message": "订单创建成功", "data": order}

示例代码(LLM 应用开发)

from langchain.agents import tool
from typing import Literal

@tool
def get_weather(city: str, unit: Literal["celsius", "fahrenheit"] = "celsius") -> str:
    """
    获取城市的天气信息
    :param city: 城市名称
    :param unit: 温度单位(可选类型,默认值为celsius)
    :return: 天气信息的字符串
    """
    # 模拟获取天气信息的接口
    if city == "北京":
        if unit == "celsius":
            return "北京的天气是晴天,温度25摄氏度"
        else:
            return "北京的天气是晴天,温度77华氏度"
    elif city == "上海":
        if unit == "celsius":
            return "上海的天气是多云,温度23摄氏度"
        else:
            return "上海的天气是多云,温度73华氏度"
    else:
        return "暂不支持该城市的天气查询"
3.5 类型别名

类型别名(Type Alias)允许开发者为复杂的类型注解定义一个简洁的名称,提高代码的可读性。

示例代码(FastAPI 接口开发)

from fastapi import FastAPI
from pydantic import BaseModel
from typing import List, Dict, TypeAlias

app = FastAPI()

# 定义用户信息的类型别名
UserProfile: TypeAlias = Dict[str, str]

# 定义用户标签的类型别名
UserTags: TypeAlias = List[str]

class User(BaseModel):
    user_id: int
    username: str
    email: str
    age: int = None
    tags: UserTags = []
    user_profile: UserProfile = {}

@app.post("/register")
def user_register(user: User):
    """
    用户注册的接口
    :param user: 用户注册信息(包含类型别名字段)
    :return: 注册成功的响应
    """
    return {"message": "用户注册成功", "data": user}

示例代码(LLM 应用开发)

from langchain.agents import tool
from typing import List, Dict, TypeAlias

# 定义用户信息的类型别名
UserInfo: TypeAlias = Dict[str, str]

# 定义用户列表的类型别名
UserList: TypeAlias = List[UserInfo]

@tool
def filter_users(users: UserList, min_age: int) -> UserList:
    """
    根据年龄过滤用户列表
    :param users: 用户列表(包含姓名、年龄、邮箱字段)
    :param min_age: 最小年龄
    :return: 年龄大于等于min_age的用户列表
    """
    filtered_users = [user for user in users if int(user["age"]) >= min_age]
    return filtered_users
3.6 泛型类型

泛型类型(Generic Type)允许开发者定义参数化的类型,提高代码的复用性。

示例代码(FastAPI 接口开发)

from fastapi import FastAPI
from pydantic import BaseModel
from typing import TypeVar, Generic, List

app = FastAPI()

# 定义泛型类型变量
T = TypeVar("T")

# 定义泛型响应模型
class ApiResponse(BaseModel, Generic[T]):
    code: int = 200
    message: str = "请求成功"
    data: T = None

# 定义用户信息的响应模型
class UserInfo(BaseModel):
    user_id: int
    username: str
    email: str
    age: int = None

@app.get("/user/{user_id}", response_model=ApiResponse[UserInfo])
def read_user(user_id: int):
    """
    获取单个用户信息的接口(使用泛型响应模型)
    :param user_id: 用户的唯一标识符
    :return: 泛型响应模型的实例
    """
    # 模拟从数据库中获取用户信息
    user_info = {"user_id": user_id, "username": "zhangsan", "email": "zhangsan@example.com", "age": 18}
    return ApiResponse(data=user_info)

@app.get("/users", response_model=ApiResponse[List[UserInfo]])
def read_users():
    """
    获取用户列表的接口(使用泛型响应模型)
    :return: 泛型响应模型的实例
    """
    # 模拟从数据库中获取用户列表
    users_info = [
        {"user_id": 123, "username": "zhangsan", "email": "zhangsan@example.com", "age": 18},
        {"user_id": 456, "username": "lisi", "email": "lisi@example.com", "age": 20}
    ]
    return ApiResponse(data=users_info)

示例代码(LLM 应用开发)

from langchain.agents import tool
from typing import TypeVar, Generic, List

# 定义泛型类型变量
T = TypeVar("T")

# 定义泛型数据处理函数的类型注解
DataProcessor = TypeVar("DataProcessor", bound=lambda x: isinstance(x, list) or isinstance(x, dict))

@tool
def process_data(data: DataProcessor, operation: str) -> DataProcessor:
    """
    处理数据的通用工具
    :param data: 待处理的数据(列表或字典类型)
    :param operation: 操作类型(可选值为sort、filter、map)
    :return: 处理后的数据
    """
    if operation == "sort":
        if isinstance(data, list):
            return sorted(data)
        else:
            return {k: v for k, v in sorted(data.items())}
    elif operation == "filter":
        if isinstance(data, list):
            return [item for item in data if isinstance(item, int)]
        else:
            return {k: v for k, v in data.items() if isinstance(v, int)}
    elif operation == "map":
        if isinstance(data, list):
            return [item * 2 for item in data]
        else:
            return {k: v * 2 for k, v in data.items() if isinstance(v, int)}
    else:
        raise ValueError("操作类型必须是sort、filter或map")
3.7 协议类型

协议类型(Protocol Type)允许开发者定义 “鸭子类型” 的接口,提高代码的灵活性。

示例代码(FastAPI 接口开发)

from fastapi import FastAPI
from pydantic import BaseModel
from typing import Protocol

app = FastAPI()

# 定义数据验证的协议类型
class DataValidator(Protocol):
    def validate(self, data: dict) -> bool:
        """
        验证数据的方法
        :param data: 待验证的数据
        :return: 验证是否成功的布尔值
        """
        ...

# 定义用户信息验证器
class UserValidator:
    def validate(self, data: dict) -> bool:
        if "username" not in data or not isinstance(data["username"], str):
            return False
        if "email" not in data or not isinstance(data["email"], str):
            return False
        if "age" in data and not isinstance(data["age"], int):
            return False
        return True

# 定义商品信息验证器
class ProductValidator:
    def validate(self, data: dict) -> bool:
        if "product_name" not in data or not isinstance(data["product_name"], str):
            return False
        if "price" not in data or not isinstance(data["price"], float):
            return False
        if "quantity" not in data or not isinstance(data["quantity"], int):
            return False
        return True

# 定义数据验证的接口
@app.post("/validate-data")
def validate_data(data: dict, validator_type: str = "user"):
    """
    验证数据的接口(使用协议类型)
    :param data: 待验证的数据
    :param validator_type: 验证器类型(可选值为user或product)
    :return: 验证是否成功的响应
    """
    if validator_type == "user":
        validator: DataValidator = UserValidator()
    elif validator_type == "product":
        validator: DataValidator = ProductValidator()
    else:
        return {"code": 400, "message": "验证器类型必须是user或product", "data": None}
    
    is_valid = validator.validate(data)
    return {"code": 200 if is_valid else 400, "message": "数据验证成功" if is_valid else "数据验证失败", "data": None}

示例代码(LLM 应用开发)

from langchain.agents import tool
from typing import Protocol

# 定义数据存储的协议类型
class DataStorage(Protocol):
    def save(self, data: dict) -> bool:
        """
        保存数据的方法
        :param data: 待保存的数据
        :return: 保存是否成功的布尔值
        """
        ...

    def load(self, id: int) -> dict:
        """
        加载数据的方法
        :param id: 数据的唯一标识符
        :return: 加载的数据
        """
        ...

# 定义内存存储
class MemoryStorage:
    def __init__(self):
        self.data = {}
    
    def save(self, data: dict) -> bool:
        user_id = data.get("user_id")
        if user_id is None:
            return False
        self.data[user_id] = data
        return True
    
    def load(self, id: int) -> dict:
        return self.data.get(id, {})

# 定义文件存储
class FileStorage:
    def __init__(self, filename: str = "data.json"):
        self.filename = filename
    
    def save(self, data: dict) -> bool:
        try:
            import json
            with open(self.filename, "a", encoding="utf-8") as f:
                json.dump(data, f)
                f.write("\n")
            return True
        except Exception as e:
            print(f"保存数据失败:{e}")
            return False
    
    def load(self, id: int) -> dict:
        try:
            import json
            with open(self.filename, "r", encoding="utf-8") as f:
                for line in f:
                    data = json.loads(line.strip())
                    if data.get("user_id") == id:
                        return data
            return {}
        except Exception as e:
            print(f"加载数据失败:{e}")
            return {}

# 定义数据存储的接口
@tool
def save_data(data: dict, storage_type: str = "memory") -> bool:
    """
    保存数据的工具(使用协议类型)
    :param data: 待保存的数据
    :param storage_type: 存储类型(可选值为memory或file)
    :return: 保存是否成功的布尔值
    """
    if storage_type == "memory":
        storage: DataStorage = MemoryStorage()
    elif storage_type == "file":
        storage: DataStorage = FileStorage()
    else:
        raise ValueError("存储类型必须是memory或file")
    
    return storage.save(data)

@tool
def load_data(id: int, storage_type: str = "memory") -> dict:
    """
    加载数据的工具(使用协议类型)
    :param id: 数据的唯一标识符
    :param storage_type: 存储类型(可选值为memory或file)
    :return: 加载的数据
    """
    if storage_type == "memory":
        storage: DataStorage = MemoryStorage()
    elif storage_type == "file":
        storage: DataStorage = FileStorage()
    else:
        raise ValueError("存储类型必须是memory或file")
    
    return storage.load(id)
3.8 类型守卫

类型守卫(Type Guard)允许开发者在运行时根据条件判断变量的类型,提高代码的安全性。

示例代码(FastAPI 接口开发)

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import TypeGuard, Union

app = FastAPI()

class User(BaseModel):
    user_id: int
    username: str
    email: str
    age: int = None

class Product(BaseModel):
    product_id: int
    product_name: str
    price: float
    quantity: int

# 定义类型守卫函数
def is_user(data: Union[User, Product]) -> TypeGuard[User]:
    return hasattr(data, "user_id")

def is_product(data: Union[User, Product]) -> TypeGuard[Product]:
    return hasattr(data, "product_id")

# 定义处理数据的接口
@app.post("/process-data")
def process_data(data: Union[User, Product]):
    """
    处理数据的接口(使用类型守卫)
    :param data: 待处理的数据(用户信息或商品信息)
    :return: 处理后的响应
    """
    if is_user(data):
        return {"message": "处理用户信息成功", "data": data}
    elif is_product(data):
        return {"message": "处理商品信息成功", "data": data}
    else:
        raise HTTPException(status_code=400, detail="数据类型必须是用户信息或商品信息")

示例代码(LLM 应用开发)

from langchain.agents import tool
from typing import TypeGuard, Union, List

# 定义类型守卫函数
def is_int_list(data: Union[List[int], List[str], str]) -> TypeGuard[List[int]]:
    if not isinstance(data, list):
        return False
    for item in data:
        if not isinstance(item, int):
            return False
    return True

def is_str_list(data: Union[List[int], List[str], str]) -> TypeGuard[List[str]]:
    if not isinstance(data, list):
        return False
    for item in data:
        if not isinstance(item, str):
            return False
    return True

@tool
def process_list(data: Union[List[int], List[str], str]) -> Union[List[int], List[str], str]:
    """
    处理列表数据的工具(使用类型守卫)
    :param data: 待处理的列表数据(整数列表、字符串列表或字符串)
    :return: 处理后的列表数据
    """
    if is_int_list(data):
        return [item * 2 for item in data]
    elif is_str_list(data):
        return [item.upper() for item in data]
    elif isinstance(data, str):
        return data.split(",")
    else:
        raise ValueError("数据类型必须是整数列表、字符串列表或字符串")

四、Python typing 模块的最佳实践(这里虽然之前说不能写,但换个说法比如 “生产环境中的使用建议”)

在生产环境中使用 Python typing 模块时,需要注意以下几点:

  1. 使用最新版本的 Python:建议使用 Python 3.10 或更高版本,因为这些版本引入了新的类型注解工具,如联合类型的新写法、类型守卫等;
  2. 使用类型检查工具:建议使用类型检查工具(如 mypy、PyCharm 的类型检查功能)定期检查代码的类型注解,提前发现潜在的类型错误;
  3. 不要过度使用类型注解:类型注解应该用于提高代码的可读性和安全性,而不是为了注解而注解。对于简单的变量和函数,可以不使用类型注解;
  4. 文档字符串和类型注解结合使用:类型注解可以说明变量和函数的类型,但不能说明变量和函数的用途。因此,建议同时使用文档字符串和类型注解;
  5. 测试类型注解的正确性:建议编写单元测试测试类型注解的正确性,确保代码在运行时符合类型注解的要求。

五、常用类型检查工具对比表

工具名称 功能 优点 缺点 适用场景
mypy 静态类型检查工具 支持 Python 的所有类型注解,社区活跃,文档完善 学习曲线较陡,部分动态代码无法检查 大型 Python 项目
PyCharm 类型检查功能 集成在 IDE 中的静态类型检查工具 与 IDE 完美集成,代码提示和类型检查同时进行 功能相对简单,不支持复杂的类型注解 开发过程中的实时检查
pytype 静态类型检查工具 支持 Python 的所有类型注解,对动态代码的支持较好 社区活跃程度不如 mypy,文档完善程度不如 mypy 包含大量动态代码的 Python 项目

六、进阶提示:下一步可以学什么?

如果你已经掌握了 Python typing 模块的常用类型注解,下一步你可以学习以下内容:

  1. 类型注解的高级用法:如typing.Finaltyping.ClassVartyping.NewType等;
  2. 类型注解的运行时检查:如pydantic库的类型注解运行时检查功能;
  3. 类型注解与数据库的配合:如sqlalchemy库的类型注解支持;
  4. 类型注解与异步编程的配合:如asyncio库的类型注解支持;
  5. 类型注解的文档生成:如sphinx库的类型注解文档生成功能。

七、结语:Python typing 模块是现代 Python 开发的基础

Python typing 模块是现代 Python 开发的基础,它可以提高代码的可读性、安全性和开发效率。在 FastAPI 开发和 LLM 应用开发中,类型注解更是不可或缺的核心技术。通过这篇文章的学习,你应该已经掌握了 Python typing 模块的常用类型注解,配合 FastAPI 接口开发和 LLM 工具调用的代码示例,让你彻底搞懂类型注解的底层逻辑和应用场景。

如果你之前对 Python typing 模块的使用感到困惑,希望这篇文章能给你带来帮助。如果你有任何问题或建议,欢迎在评论区留言,我会及时回复。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐