如何为动态创建的Python类方法添加类型注解
先修正原代码的一个潜在问题:request方法中data: dict = {}使用了可变对象作为默认参数,多次调用时若修改data会影响后续请求,建议改成:
def request(self, method: str, path: str, data: dict | None = None, files: dict | None = None) -> Response: data = data or {} # 原请求逻辑代码
下面是完整的类型注解优化方案,让send_test、send_start这类动态方法能被静态检查器识别,且仅接受data和files参数:
完整代码示例
from typing import TypedDict, Optional, Callable, TYPE_CHECKING # 定义响应结构类型 class Response(TypedDict): status: int content: str # 示例字段,根据实际业务调整 # 路由映射配置 ROUTES = { "send_test": ["GET", "/test/"], "send_start": ["GET", "/start/"] } # 定义动态请求方法的类型别名:接受data和files,返回Response RequestAlias = Callable[[dict, Optional[dict]], Response] # 仅在静态类型检查时生效,告知检查器Client类存在这些动态方法 if TYPE_CHECKING: class Client: send_test: RequestAlias send_start: RequestAlias class Client(object): def __init__(self) -> None: for mname, (method, path) in ROUTES.items(): setattr(self, mname, self.make_request_func(method, path)) def make_request_func(self, method: str, path: str) -> RequestAlias: # 给闭包明确标注参数和返回值类型 def _func(data: dict = {}, files: Optional[dict] = None) -> Response: return self.request(method, path, data, files) return _func def request(self, method: str, path: str, data: dict = {}, files: Optional[dict] = None) -> Response: # 实际请求逻辑,示例返回值 return {"status": 200, "content": "OK"}
关键注解解释
RequestAlias类型别名
用Callable[[dict, Optional[dict]], Response]统一定义动态方法的签名:接受data(字典)和files(可选字典)两个参数,返回Response类型,避免重复写复杂的Callable类型。TYPE_CHECKING条件块TYPE_CHECKING是运行时为False的常量,仅静态检查器(如mypy)会解析这块代码。在这里给Client类添加动态方法的类型声明,让检查器识别这些方法的存在和签名,不会抛出“属性未定义”的错误。闭包
_func的注解
给动态生成的函数明确标注参数和返回值类型,既保证和RequestAlias类型匹配,也让静态检查器能识别该函数的参数要求。request方法的参数注解
给method、path添加str类型注解,明确必填参数类型;files用Optional[dict]标注,表示该参数可以为None或字典。
批量路由的简化优化
如果后续路由会频繁新增,手动在TYPE_CHECKING块添加方法注解太繁琐,可以用__getattr__自动处理:
from typing import Any class Client(object): def __getattr__(self, name: str) -> RequestAlias: if name in ROUTES: method, path = ROUTES[name] return self.make_request_func(method, path) raise AttributeError(f"Client has no attribute '{name}'") # 其他方法保持不变...
这样静态检查器会通过__getattr__的返回值类型,自动识别所有ROUTES中的方法都符合RequestAlias签名,无需逐个添加注解。
内容的提问来源于stack exchange,提问作者itekhi

