从通读
mypy
问题来看,似乎调用 dict.update()
,并且提供 TypedDict
不是类型安全的。 这对我来说没有意义。
我的问题是(特别是第二期,链接如下):
TypedDict
在运行时是 dict
,为什么 mypy
抱怨无法将 TypedDict
传递到 dict.update
中,说它需要 Mapping
?
dict
就像说Dict[Any, Any]
,那么为什么不添加一个TypedDict
作为字典值呢?dict.update(SomeTypedDict)
类型不安全?在
mypy
问题中找到了两个这样的例子:
这是一个非常微妙的问题。更新调用可以说不是类型安全的。
由于 TypedDict 对象使用结构子类型,因此可能存在通过类型
不可见的其他项目,具有任意值类型。因此Foo
是正确的(尽管它不直观)。Mapping[str, object]
示例代码来自 python/mypy #9086
from typing import TypedDict
class Foo(TypedDict):
baz: int
foo: Foo = {"baz": 9000}
# spam is a regular dict, yet mypy errors out when trying to add a TypedDict
# to it. This doesn't make sense to me, when a regular dict should be like
# saying equal Dict[Any, Any]
spam = {"ham": {"eggs": 5}}
spam["ham"].update(foo) # error: Argument 1 to "update" of "dict" has
# incompatible type "Foo"; expected "Mapping[str, int]" [arg-type]
PEP 589 说:
首先,任何
类型都与TypedDict
一致。其次,如果 A 在结构上与 B 兼容,则Mapping[str, object]
类型 A 与TypedDict
B 一致。TypedDict
包含所有 int 值的示例:
TypedDict
与Mapping[str, int]
不一致,因为由于结构子类型化,可能存在通过类型不可见的其他非 int 值。例如,可以使用映射中的values()和items()方法来访问这些。
class A(TypedDict):
x: int
class B(TypedDict):
x: int
y: str
def sum_values(m: Mapping[str, int]) -> int:
n = 0
for v in m.values():
n += v # Runtime error
return n
def f(a: A) -> None:
sum_values(a) # Error: 'A' incompatible with Mapping[str, int]
b: B = {'x': 0, 'y': 'foo'}
f(b)
from typing import TypedDict
class Foo(TypedDict):
baz: int
foo: Foo = {"baz": 9000}
# spam here is not equal Dict[Any, Any]. mypy will infer type for it
# as Dict[str, Dict[str, int]]. Consequently, update() method for its
# item below will expect Mapping[str, int]
spam = {"ham": {"eggs": 5}}
# If you try to do empty dict as below which indeed has type Dict[Any, Any]
# spam = {} # mypy will complain on it "Need type annotation for 'spam'"
spam["ham"].update(foo) # error: Argument 1 to "update" of "dict" has
# incompatible type "Foo"; expected "Mapping[str, int]" [arg-type]