Mypy:在映射类型中使用联合无法按预期工作
Mypy: Using unions in mapping types does not work as expected
考虑以下代码:
def foo(a: dict[str | tuple[str, str], str]) -> None:
pass
def bar(b: dict[str, str]) -> None:
foo(b)
def baz(b: dict[tuple[str, str], str]) -> None:
foo(b)
foo({"foo": "bar"})
foo({("foo", "bar"): "bar"})
在严格模式下使用 mypy 检查时会产生以下错误:
file.py:6: error: Argument 1 to "foo" has incompatible type "Dict[str, str]"; expected "Dict[Union[str, Tuple[str, str]], str]"
file.py:9: error: Argument 1 to "foo" has incompatible type "Dict[Tuple[str, str], str]"; expected "Dict[Union[str, Tuple[str, str]], str]"
这对我来说似乎没有意义。该参数被定义为接受 dict
,其中字符串或元组作为键,字符串作为值。但是,当明确注释时,这两种变体都不被接受。然而,当将这样的字典直接传递给函数时,它们确实有效。在我看来,mypy 期望 has 能够将联合的两个选项作为键。我不明白为什么?如果键的约束是字符串或字符串的元组,则传递任何一个都应该没问题。正确的?我在这里遗漏了什么吗?
A dict[str | tuple[str, str], str]
不仅仅是带有 str
或 tuple[str, str]
键的字典。这是一个字典,您可以 添加更多 str
或 tuple[str, str]
键。
您不能将 str
键添加到 dict[tuple[str, str], str]
,也不能将 tuple[str, str]
键添加到 dict[str, str]
,因此这些类型不是兼容。
如果你直接传递一个字面量字典给foo
(或bar
或baz
),那个字面量没有静态类型。 mypy 根据上下文推断字典的类型。可以根据上下文为文字推断出许多不同的类型。当您在 bar
或 baz
中将 b
传递给 foo
时,b
已经具有静态类型,并且该类型与 foo
不兼容'签名.
这里有用的是
def foo(a: dict[str, str] | dict[tuple[str, str], str]) -> None:
pass
或者你必须帮助 mypy 并显式地键入注释你传递的字典
mydict : dict[str | tuple[str, str], str] = {"a" : "b"}
foo(mydict) # your foo as typed in your example
所以,我终于弄清楚了问题所在。正如我在 @user2357112 supports Monica's answer, Mapping
is in fact invariant on the key 之后所怀疑的那样。除了由于 Mapping
本身的实现方式而显然难以实现之外,没有充分的理由这样做。
考虑以下代码:
def foo(a: dict[str | tuple[str, str], str]) -> None:
pass
def bar(b: dict[str, str]) -> None:
foo(b)
def baz(b: dict[tuple[str, str], str]) -> None:
foo(b)
foo({"foo": "bar"})
foo({("foo", "bar"): "bar"})
在严格模式下使用 mypy 检查时会产生以下错误:
file.py:6: error: Argument 1 to "foo" has incompatible type "Dict[str, str]"; expected "Dict[Union[str, Tuple[str, str]], str]"
file.py:9: error: Argument 1 to "foo" has incompatible type "Dict[Tuple[str, str], str]"; expected "Dict[Union[str, Tuple[str, str]], str]"
这对我来说似乎没有意义。该参数被定义为接受 dict
,其中字符串或元组作为键,字符串作为值。但是,当明确注释时,这两种变体都不被接受。然而,当将这样的字典直接传递给函数时,它们确实有效。在我看来,mypy 期望 has 能够将联合的两个选项作为键。我不明白为什么?如果键的约束是字符串或字符串的元组,则传递任何一个都应该没问题。正确的?我在这里遗漏了什么吗?
A dict[str | tuple[str, str], str]
不仅仅是带有 str
或 tuple[str, str]
键的字典。这是一个字典,您可以 添加更多 str
或 tuple[str, str]
键。
您不能将 str
键添加到 dict[tuple[str, str], str]
,也不能将 tuple[str, str]
键添加到 dict[str, str]
,因此这些类型不是兼容。
如果你直接传递一个字面量字典给foo
(或bar
或baz
),那个字面量没有静态类型。 mypy 根据上下文推断字典的类型。可以根据上下文为文字推断出许多不同的类型。当您在 bar
或 baz
中将 b
传递给 foo
时,b
已经具有静态类型,并且该类型与 foo
不兼容'签名.
这里有用的是
def foo(a: dict[str, str] | dict[tuple[str, str], str]) -> None:
pass
或者你必须帮助 mypy 并显式地键入注释你传递的字典
mydict : dict[str | tuple[str, str], str] = {"a" : "b"}
foo(mydict) # your foo as typed in your example
所以,我终于弄清楚了问题所在。正如我在 @user2357112 supports Monica's answer, Mapping
is in fact invariant on the key 之后所怀疑的那样。除了由于 Mapping
本身的实现方式而显然难以实现之外,没有充分的理由这样做。