List 是否有协变可变版本?

Mar*_*oma 11 python python-typing

我已经将我真正想要注释的代码简化为这个最小版本:

def print_it(numbers_or_nones):
    for i, number in enumerate(numbers_or_nones):
        if number is None:
            numbers_or_nones[i] = 0
            print("NOOOO")
        else:
            print(number)


numbers = [1, 2, 3, 4]
print_it(numbers)
Run Code Online (Sandbox Code Playgroud)

numbers_or_nones我想对的参数进行注释print_it。它需要...

  • ...元素所在的通用类型Optional[int]
  • ...可迭代
  • ...支持索引分配

对于这种情况,正确的类型是什么?请注意,无法更改 的类型numbers : List[int]。我能看到的唯一选择是使用typing.overload.

列表

最简单的事情是List[Optional[int]]。然而,这给出了:

error: Argument 1 to "print_it" has incompatible type "List[int]"; expected "List[Optional[int]]"
note: "List" is invariant -- see http://mypy.readthedocs.io/en/latest/common_issues.html#variance
note: Consider using "Sequence" instead, which is covariant
Run Code Online (Sandbox Code Playgroud)

顺序

Unsupported target for indexed assignment ("Sequence[Optional[int]]")
Run Code Online (Sandbox Code Playgroud)

可变序列

error: Argument 1 to "print_it" has incompatible type "List[int]"; expected "MutableSequence[Optional[int]]"
Run Code Online (Sandbox Code Playgroud)

Rec*_*ing 9

简短回答:不存在协变可变集合,此处列出了处理这种情况的可能策略https://mypy.readthedocs.io/en/stable/common_issues.html#invariance-vs-covariance


为什么可变集合不能协变?因为这会导致严重的问题,例如:

def make_first_None(numbers_or_nones: MutableSequence[Optional[int]]):
    numbers_or_nones[0] = None


numbers: List[int] = [1, 2, 3, 4]
make_first_None(numbers) # Error!! Numbers is not a List[int] anymore!!!
Run Code Online (Sandbox Code Playgroud)

关于为什么可变集合必须不变的更长解释在这里https://mypy.readthedocs.io/en/stable/generics.html#variance-of-generics


对于这种情况,文档中列出的三种策略如下所示:

  • 使用显式类型注释:
numbers: List[Optional[int]] = [1, 2, 3, 4]
print_it(numbers)
Run Code Online (Sandbox Code Playgroud)
  • 复印并传递
print_it(list(numbers))
Run Code Online (Sandbox Code Playgroud)
  • 使用不可变集合
print_it(list(numbers))
Run Code Online (Sandbox Code Playgroud)