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

为何使用list时mypy会对类型明确的元组列表报错?

为什么用list注解时mypy会报错?

问题复现

先看这段代码,用mypy 1.8.0检查会报错:

def foo(data: list[tuple[int, ...]]) -> None:
    pass


r = [(1, 2, 3)]
foo(r)

报错信息如下:

main.py:6: error: Argument 1 to "foo" has incompatible type "list[tuple[int, int, int]]"; expected "list[tuple[int, ...]]" [arg-type]
main.py:6: note: "List" is invariant
main.py:6: note: Consider using "Sequence" instead, which is covariant
Found 1 error in 1 file (checked 1 source file)

但两种情况却能正常通过检查:

情况1:直接传递元组

def foo(data: tuple[int, ...]) -> None:
    pass


r = (1, 2, 3)
foo(r)

检查结果:

Success: no issues found in 1 source file

情况2:用Sequence替代list

from typing import Sequence

def foo(data: Sequence[tuple[int, ...]]) -> None:
    pass


r = [(1, 2, 3)]
foo(r)

检查结果:

Success: no issues found in 1 source file


核心原因

1. list是「不变类型」

mypy对list的类型检查遵循不变性:只有两个列表的元素类型完全一致,它们的类型才兼容。

  • tuple[int, int, int]是固定长度的具体元组类型,tuple[int, ...]是任意长度的int元组类型,二者属于不同的子类型。
  • 因为list是不变的,list[tuple[int, int, int]]不能被当成list[tuple[int, ...]]来用,所以mypy报错。

2. 元组本身是「协变类型」

元组的类型规则是协变的:

  • 固定长度的tuple[int, int, int]是tuple[int, ...]的子类,它满足“所有元素都是int”的要求。
  • 协变类型允许把子类类型赋值给父类类型,所以直接传元组不会有问题。

3. Sequence是「协变类型」

Sequence是抽象序列类型,设计时就被定义为协变:

  • 如果类型A是类型B的子类,那么Sequence[A]可以视为Sequence[B]的子类。
  • 所以Sequence[tuple[int, int, int]]和Sequence[tuple[int, ...]]是兼容的,用Sequence注解就能通过检查。

解决方案建议

  • 优先用Sequence这类协变的抽象类型,既满足类型安全,又能兼容子类元素的序列。
  • 如果必须用list,可以通过cast(list[tuple[int, ...]], r)做类型转换,或者调整函数注解为list[tuple[int, ...] | tuple[int, int, int]],但前者更符合类型系统的设计逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 09:05:31