如何基于已有类型构建新函数类型别名并通过mypy类型检查?
如何基于已有类型构建新函数类型别名并通过mypy类型检查?
我懂你这种需求——想基于已经定义好的函数类型别名,只加个参数就生成新的类型,结果直接操作__args__在运行时凑出来的类型,mypy根本不认对吧?这是因为静态类型检查器不吃这种动态属性操作的写法,得用类型系统里合法的构造来实现。
推荐方案:用ParamSpec和Concatenate实现参数扩展
Python 3.10及以上版本(或者通过typing-extensions库兼容旧版本)提供了ParamSpec和Concatenate,这正是为扩展函数参数签名专门设计的特性,mypy完全认可这种写法。
针对你的场景,代码可以这么写:
from typing import Callable, ParamSpec, Concatenate # 先定义一个ParamSpec来捕获原始函数的参数签名 P = ParamSpec("P") # 原始函数类型:绑定ParamSpec和返回值 RK_function = Callable[P, int] # 扩展后的函数类型:把原始参数和新增的int拼接,返回值保持int RK_functionBIS = Callable[Concatenate[*P, int], int]
这样定义后,RK_functionBIS就等价于你想要的Callable[[float, int, int], int],而且mypy会完全通过类型检查。
你可以用下面的代码验证一下:
def test_func(a: float, b: int, c: int) -> int: return int(a) + b + c # 类型检查完全通过 bis_func: RK_functionBIS = test_func
为什么你原来的写法不行?
你直接操作RK_function.__args__[:-1]这种方式,属于运行时动态属性访问,而mypy作为静态类型检查器,只会解析Python类型系统定义的合法语法,不会处理这种运行时才存在的属性操作,所以会抛出“Invalid type alias”的错误。
兼容旧版本的替代方案
如果你的Python版本低于3.10,又不想引入typing-extensions,可以把原始类型的参数拆成单独的类型变量来复用:
from typing import Callable, TypeVar # 定义原始类型用到的类型变量 T1 = TypeVar("T1", bound=float) T2 = TypeVar("T2", bound=int) # 原始函数类型 RK_function = Callable[[T1, T2], int] # 扩展后的函数类型:在原有类型变量后新增int参数 RK_functionBIS = Callable[[T1, T2, int], int]
不过这种方式的灵活性差一些,如果原始函数的参数很多,写起来会比较繁琐,所以还是优先推荐ParamSpec+Concatenate的方案。
备注:内容来源于stack exchange,提问作者Pierre-olivier Gendraud
相关产品推荐
相关产品推荐

