为何使用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
相关产品推荐
相关产品推荐

