You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决将泛型函数作为参数时的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自身定义了同名的T TypeVar,这个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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 23:30:38