我有一个脚本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
此行为与mypy使用上下文推断类型的方式有关。调用函数时,函数定义中参数的类型提示可用于推断传递的自变量的类型。
然而,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]"