List 在 Python 中真的是不变的吗

问题描述 投票:0回答:1

我有一个脚本

toy.py
,我发现它的行为有点令人困惑。

from typing import List


class A:
    a = 1

class B(A):
    b = 2


def func(input_arg: List[A]) -> None:
    """Debugging mypy."""
    print(f"{input_arg=:}")
    print(f"{type(input_arg)=:}")

如果我追加

if __name__ == "__main__":
    arg1 = B()
    reveal_type([arg1])
    func([arg1])

mypy 通过:

mypy toy.py
toy.py:22:17: note: Revealed type is "builtins.list[toy.B*]"

但是如果我改为追加

if __name__ == "__main__":
    arg2 = [B()]
    reveal_type(arg2)
    func(arg2)

我认为这相当于第一种情况,我看到错误

mypy toy.py
toy.py:26:17: note: Revealed type is "builtins.list[toy.B*]"
toy.py:27:10: error: Argument 1 to "func" has incompatible type "List[B]"; expected "List[A]"
toy.py:27:10: note: "List" is invariant -- see https://mypy.readthedocs.io/en/stable/common_issues.html#variance
toy.py:27:10: note: Consider using "Sequence" instead, which is covariant
Found 1 error in 1 file (checked 1 source file)

如果 List 不变,为什么第一种情况会通过?

mypy --version
mypy 0.931
python python-typing mypy
1个回答
3
投票

此行为与 mypy 使用上下文推断类型的方式有关。调用函数时,函数定义中parameters的类型提示可用于推断传递的arguments的类型。

但是,mypy 只允许这种“基于上下文的推理”在单个语句中mypy 文档中的以下示例说明了更极端的情况:

这是允许的,并使用单语句上下文来推断空列表的类型为

list[int]
:

def foo(arg: list[int]) -> None:
   print('Items:', ''.join(str(a) for a in arg))

foo([])  # OK

但是在这里,上下文需要从语句

foo(a)
向上过滤到赋值
a = []
,类似于您的第二个示例,但 mypy 无法做到这一点。

def foo(arg: list[int]) -> None:
   print('Items:', ''.join(str(a) for a in arg))

a = []  # Error: Need type annotation for "a"
foo(a)

有趣的是,使用赋值表达式也不起作用:赋值语句在调用函数之前完成,因此无法传递上下文:

def foo(arg: list[int]) -> None:
   print('Items:', ''.join(str(a) for a in arg))

foo(a := [1.1])  # Error: "a" has incompatible type "list[float]"
© www.soinside.com 2019 - 2024. All rights reserved.