如何解决将泛型函数作为参数时的mypy [arg-type]错误?
泛型函数传入apply时的mypy类型错误解决
问题场景
希望将泛型函数作为参数传入关联了具体类型的apply函数,让泛型函数的类型注解决定输出类型。但使用签名为(T) -> T的泛型函数时,触发了mypy的[arg-type]错误:
Argument 1 to "apply" of "C" has incompatible type "Callable[[T], T]"; expected "Callable[[float], T]" [arg-type]
复现代码
from typing import Callable, TypeVar, Generic T = TypeVar("T") U = TypeVar("U") class C(Generic[T]): def __init__(self, val: T): self.val: T = val def apply(self, fn: Callable[[T], U]) -> U: return fn(self.val) class FloatC(C[float]): pass def fa(x: T) -> T: return x def fb(x: T) -> str: return "hello" c = FloatC(1) a = c.apply(fa) b = c.apply(fb) reveal_type(a) reveal_type(b)
mypy输出
test.py:25: error: Argument 1 to "apply" of "C" has incompatible type "Callable[[T], T]"; expected "Callable[[float], T]" [arg-type] test.py:28: note: Revealed type is "T`-1" test.py:29: note: Revealed type is "builtins.str" Found 1 error in 1 file (checked 1 source file)
期望mypy能识别出a的类型为float且不报错,问题出在哪?该如何解决?
问题原因
核心冲突在于TypeVar的作用域和绑定顺序:
- 类
C的apply方法中,T是类级别的TypeVar,已绑定到FloatC的float类型,U是方法级TypeVar,会根据传入函数的返回值推导。 - 泛型函数
fa自身定义了同名的TTypeVar,这个T和类中的T是完全独立的变量。mypy检查时,无法确认fa的T能否和apply要求的float绑定,因此抛出类型不兼容错误。
解决方法
方法1:明确泛型函数的TypeVar作用域(推荐)
让泛型函数的TypeVar与类的TypeVar完全分离,确保mypy能正确推导类型匹配:
适用于Python 3.12+(支持PEP 695泛型语法)
from typing import Callable, TypeVar, Generic T = TypeVar("T") U = TypeVar("U") class C(Generic[T]): def __init__(self, val: T): self.val: T = val def apply(self, fn: Callable[[T], U]) -> U: return fn(self.val) class FloatC(C[float]): pass # 使用PEP 695语法定义泛型函数,TypeVar作用域明确 def fa[T](x: T) -> T: return x def fb(x: T) -> str: return "hello" c = FloatC(1) a = c.apply(fa) b = c.apply(fb) reveal_type(a) # 输出:builtins.float reveal_type(b) # 输出:builtins.str
适用于Python 3.11及以下
from typing import Callable, TypeVar, Generic T = TypeVar("T") U = TypeVar("U") # 为泛型函数单独定义TypeVar,避免和类的T冲突 FnT = TypeVar("FnT") class C(Generic[T]): def __init__(self, val: T): self.val: T = val def apply(self, fn: Callable[[T], U]) -> U: return fn(self.val) class FloatC(C[float]): pass def fa(x: FnT) -> FnT: return x def fb(x: T) -> str: return "hello"
方法2:修改apply方法的签名以支持泛型推导
使用ParamSpec和TypeVar更灵活地匹配函数参数与返回值:
from typing import Callable, TypeVar, Generic, ParamSpec T = TypeVar("T") P = ParamSpec("P") R = TypeVar("R") class C(Generic[T]): def __init__(self, val: T): self.val: T = val # 用ParamSpec和R匹配任意Callable的参数和返回值 def apply(self, fn: Callable[P, R]) -> R: return fn(self.val) class FloatC(C[float]): pass def fa(x: T) -> T: return x def fb(x: T) -> str: return "hello" c = FloatC(1) a = c.apply(fa) # 推导类型为float,无错误 b = c.apply(fb) # 推导类型为str reveal_type(a) reveal_type(b)
内容的提问来源于stack exchange,提问作者Finix
相关产品推荐
相关产品推荐

