我正在尝试注释一个仅返回两个值的迭代器,
T
和cls[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 方法将always产生两个值来简化用户的过程。
事实上,用户必须处理以下问题:
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源)这意味着其__next__
方法返回的每个值都必须具有相同的类型。
您正在对
self.value, self.next
的元组调用迭代器协议。元组具有任意数量的类型参数(请参阅此处),但其迭代器仍必须恰好有一个。这实际上经常导致打字问题。
由于您似乎打算让您的
Node
类本质上模拟 tuple
接口,这可能是极少数情况之一,最好直接从它继承。显然,tuple
还会为您提供可迭代协议,因此您仍然可以像以前一样解压它,但如果您正确执行所有操作,则应该正确推断类型。
这是一个完整的工作示例:
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
指定类型参数,因为它会自动 bound 到传递给 value
参数的类型。
for while 是可以用作通用迭代器注释的循环