python - 通用迭代器注释 Python

我正在尝试注释一个只​​返回两个值的迭代器,Tcls[T]

目前我有这样的注释:

from __future__ import annotations
import typing


class Node(typing.Generic[T]):
    def __init__(self, value: T, next: typing.Optional[Node[T]] = None) -> None:
        self.value = value 
        self.next = next

    def __iter__(self) -> typing.Iterator[typing.Union[T, Node[T]]]:
        yield from (self.value, self.next)

这段代码没有任何错误。但是,我希望利用这个 dunder 方法总是产生两个值这一事实来简化用户的过程。

实际上,用户必须处理以下内容:

one = Node[int](1, Node[int](2))
value, next = one # value = 1, next = Node(2) (i.e one.next)

# This is a typing error because next can be either int or Node[int] 
# and 'two' is expected to be Node[int]:
two: Node[int] = next

# we can fix this by doing type narrowing
assert isinstance(next, Node)
two = next # now the error should be gone

所以基本上我想利用这样一个事实,即 __iter__ 返回的第二个东西总是类型 Node[T] 以避免必须进行类型缩小.

我知道我必须在方法的返回注释中更改 typing.Union[T, Node[T]],但我不知道将其更改为什么。

最佳答案

这不可能使用通用的 Iterator 进行注释。它是一个需要 one 类型参数的类。 (当前 typeshed source )这意味着其 __next__ 方法返回的每个 值必须具有相同的类型。

您正在 self.value, self.next 元组上调用迭代器协议(protocol)。一个元组有任意 数量的类型参数(参见 here ),但它上面的迭代器必须只有一个。这实际上经常导致打字问题。

由于您似乎打算让您的 Node 类从本质上模拟 tuple 接口(interface),这可能是极少数情况之一,最好直接从它继承. tuple 显然也会为您提供可迭代协议(protocol),因此您仍然可以像以前一样解压它,但是如果您做的一切都正确,则应该正确推断类型。

这是一个完整的工作示例:

from __future__ import annotations
from typing import TypeVar, Optional


T = TypeVar("T")


class Node(tuple[T, "Node[T]"]):
    def __new__(cls, value: T, next_: Optional[Node[T]] = None) -> Node[T]:
        return tuple.__new__(cls, (value, next_))

    def __init__(self, value: T, next_: Optional[Node[T]] = None) -> None:
        self.value = value
        self.next = next_


if __name__ == "__main__":
    node_1 = Node(1, Node(2))
    val: int
    node_2: Node[int]
    val, node_2 = node_1

这通过了 mypy --strict 没有问题。


作为不相关的旁注,我建议不要使用内置名称,如 next .

另外,请注意,当您初始化一个节点时,您不需要为 Node 指定类型参数,因为它会自动绑定(bind)到传递给 的类型>值参数。

https://stackoverflow.com/questions/74696410/

相关文章:

c++ - Clang 静态链接 libc++

swift - 如何为等待函数调用添加超时

haskell - 如何测试自定义 StateT 的 Monad 实例?

typescript - 如何在保持默认行为的同时扩展 tsconfig 中的类型

windows-installer - 在 Windows 的添加/删除程序中显示正确的大小

javascript - 是的,当 .required() 为 false 时,会继续在同一个属性上

laravel - 生产模式编译报错 "is not a function"Vue Laravel

node.js - 为什么当我使用 "waitForSelector"时 Puppeeteer 导致

python - Pandas dataframe,连续查找所选列中的最大值,并根据该值查找另一列的

python - ModuleNotFoundError 即使模块被识别