为什么在下面 test
not typecheck (with Mypy 0.780)?
from typing import Iterator, Tuple
xs: Iterator[int] = (i for i in (1,2,3))
ys: Iterator[int] = (i for i in (1,2,3))
xys: Iterator[Tuple[int,int]] = zip(*(xs,ys))
test: Tuple[int,int,int] = tuple(map(sum, xys))
的错误信息。
错误。赋值中的不兼容类型(表达式的类型为 "Tuple[Union[_T,int],...]",变量的类型为 "Tuple[int,int,int]")。
意见:转为 Tuple[int, ...]
删除该错误。
评论。我觉得这比以下情况更令人惊讶 test: Tuple[int,int,int] = tuple(range(3))
到 typecheck (错误信息更容易理解),但也许是同一个根本问题:长度推理。
EDIT:
对于@MisterMiyagi的第二条评论,请考虑以下内容,它也会产生同样的错误,但显然可以推断长度。
xss: Tuple[Tuple[int,int,int], ...] = tuple((1,2,3) for _ in range(10))
test: Tuple[int,int,int] = tuple(map(sum, zip(*xss)))
Python的类型系统一般不表达项数。Tuple
是唯一的例外;使用 任何类型 Tuple
不能表达项目计数,因此放弃了这种信息。简而言之,没有长度可以推断,因为Python的类型系统不知道这样的事情。
具体来说,无论是 map
和 zip
归于 Iterator[...]
(如 签章). 任何之前的项目计数信息都会被丢弃。中间类型翻译可以用mypy的 reveal_type
:
xss: Iterator[Tuple[int,int,int]] = ((1,2,3) for _ in range(10))
test: Tuple[int,int,int] = tuple(map(sum, zip(*xss)))
reveal_type(zip(*xss)) # note: Revealed type is 'typing.Iterator[builtins.tuple[Any]]'
reveal_type(map(sum, *xss)) # note: Revealed type is 'typing.Iterator[Union[_T`-1, builtins.int]]'
虽然人们可以 原则上 在迭代器之间传播项目计数,这在一般的静态类型检查中是不可靠的。基本的问题是,一个任意迭代器是一次性使用的:一个"n
大小 Iterator
"变成了一个"n-1
大小 Iterator
在一个迭代步骤后的""和一个"0
大小 Iterator
"用完后。
因此,一个变量的类型持有一个"n
大小 Iterator
"就得根据使用情况而改变。
a: Iterator[int] = iter((1, 2))
# a is "2 size Iterator"
b = tuple(a)
# a is "0 size Iterator"
c = tuple(a)