带悬垂的成对提示类型

Type hinting pairwise with overhang

提问人:J.N. 提问时间:10/5/2023 最后编辑:mkrieger1J.N. 更新时间:10/6/2023 访问量:65

问:

我接管了一个代码库(支持低至 3.9),并想添加一些类型提示。但是,我目前被困在此功能上。

def _pairwise(iterable: T.Iterable, end=None) -> T.Iterable:
    left, right = itertools.tee(iterable)
    next(right, None)
    return itertools.zip_longest(left, right, fillvalue=end)

稍后用于迭代正则表达式匹配并提取其开始和结束索引以进行切片。的最后一个填充值用于将最后一个切片转到字符串的末尾。None

我们知道实际签名应该是

_pairwise(iterable: Iterable[T], end: Optional[T] = None) -> Iterator[tuple[T, Optional[T]]]

因为保证至少和 .leftright

但是,带有 的方法不允许这样做。类型检查器将其读取为 .zip_longestIterator[Optional[T], Optional[T]]

我重写了该函数,以便类型检查器(pyright)能够验证该目标签名。

def _pairwise(
    iterable: Iterable[T], end: Optional[T] = None
) -> Iterator[tuple[T, Optional[T]]]:
    left, right = itertools.tee(iterable)
    next(right, None)
    for x, y in zip(right, left):
        yield y, x
    if (last := next(left, None)) is not None:
        yield last, end

但是,我对这个结果还不是特别满意。首先,需要交换参数以避免它采取额外的步骤,以及手动检查以处理参数是空迭代对象的情况。zipleft

这也意味着该功能不是预期的功能,尽管这实际上不是问题,但它确实让我有点恼火。T=NoneType

有没有其他方法可以让这个成对功能进行类型检查?

python-itertools python类型

评论

0赞 STerliakov 10/5/2023
在您的最后一个实现中,您可以代替进一步清理代码,但我只是放了一个忽略注释 - 这显然只是一个类型限制,如果您解释它们,忽略注释并没有错。您也可以使用 和 使用 进行右迭代,这也应该进行类型检查。yield fromfor...yielditertools.chain[end]zip
1赞 J.N. 10/5/2023
@STerliakov 我不能直接屈服,因为我必须交换屈服中元素的顺序,以免 zip 吃掉 的最后一个元素。但我想你是对的。我可能应该忽略打字错误。我想如果我可以从中产生并且不需要对空迭代对象进行检查,我会使用它。left
0赞 STerliakov 10/5/2023
这对你有用吗?这在效率和类型检查方面应该几乎相同。我认为这比 更好地表达了意图,因为您肯定知道哪个可迭代对象更长(严格来说,不是更短)。zip_longest
0赞 J.N. 10/5/2023
@STerliakov看起来也不错。当可迭代对象开始时为空时,它的行为如何?似乎做得很好,因为 zip 会立即停止,因为左侧会引发 StopIteration。似乎是完美的解决方案,谢谢。如果你想把它写成一个正确的答案,我会给你复选标记。否则我会让每个人都能清楚地看到它。

答:

0赞 STerliakov 10/6/2023 #1

您的解决方案对我来说看起来很干净,并且非常适合那里,可能会有一个简短的解释性评论。zip_longest# type: ignore[return-value]

但是,要进行代码类型检查,您可以使用 plain 并在末尾手动添加“filler”条目,如下所示:zip

import itertools
from typing import Iterable, Iterator, TypeVar, Optional

T = TypeVar('T')


def _pairwise(
    iterable: Iterable[T], end: Optional[T] = None
) -> Iterator[tuple[T, Optional[T]]]:
    left, right = itertools.tee(iterable)
    next(right, None)
    return zip(left, itertools.chain(right, [end]))

现在对这段代码很满意,而且你稍微明确一点:你知道第二个可迭代 () 比 unless is empty 短一个元素,因此附加一个项目将使它们相等。如果输入为空,则两个实现都会生成一个空迭代器。mypyrightleftiterable