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

用TypeVar定义Haskell风格fst/snd函数时mypy类型错误排查

问题分析:mypy对map函数类型检查的差异

问题背景

先看定义的Haskell风格fst和snd函数:

from typing import TypeVar
a = TypeVar("a")
b = TypeVar("b")

def fst(x: tuple[a, b]) -> a:
    """Return the first element of a pair."""
    return x[0]

def snd(x: tuple[a, b]) -> b:
    """Return the second element of a pair."""
    return x[1]

以下f函数能通过mypy检查:

from collections.abc import Iterable, Iterator

def f(xs: Iterable[tuple[str, int]]) -> Iterator[int]:
    """Return second element of string-integer pairs."""
    return map(snd, xs)

但g函数会触发mypy错误:

def g(xs: Iterable[tuple[str, int]]) -> list[int]:
    """Return second element of string-integer pairs."""
    return list(map(snd, xs))

错误信息:

error: Argument 1 to "map" has incompatible type "Callable[[Tuple[a, b]], b]"; expected "Callable[[Tuple[str, int]], b]"  [arg-type]

错误原因

核心是mypy对map的类型推导逻辑在不同场景下的差异:

  • 对于f函数,直接返回map的结果,mypy可以把map的泛型参数和函数返回值Iterator[int]做统一推导,自动将snd的类型变量a绑定为str、b绑定为int,类型匹配通过。
  • 对于g函数,list()需要接收类型明确的可迭代对象。此时mypy需要先推导map(snd, xs)的类型,但snd的类型变量还没被显式绑定,导致mypy无法确认snd的返回类型是否能对应list[int]所需的元素类型,从而抛出类型不兼容错误。

fst和snd的定义问题

这两个函数的逻辑本身没问题,但类型注解存在局限性:当前的TypeVar没有指定协变/逆变属性,也没有显式约束,导致mypy在跨上下文推导(比如map的参数被list包裹时)无法准确完成类型绑定。

解决方案

有两种可行的修正方式:

  • 给TypeVar添加协变标注:
    修改类型变量定义,让其支持协变推导,帮助mypy更灵活地绑定类型:

    from typing import TypeVar
    a = TypeVar("a", covariant=True)
    b = TypeVar("b", covariant=True)
    
    def fst(x: tuple[a, b]) -> a:
        return x[0]
    
    def snd(x: tuple[a, b]) -> b:
        return x[1]
    
  • 显式指定map的泛型参数:
    调用map时明确指定类型参数,直接帮mypy完成推导:

    def g(xs: Iterable[tuple[str, int]]) -> list[int]:
        return list(map[str, int](snd, xs))
    

内容的提问来源于stack exchange,提问作者aparkerlue

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 22:52:47