Python正在使用dict.update,如果传入TypedDict,是否不支持类型安全?

2024-03-28 10:42:09 发布

您现在位置:Python中文网/ 问答频道 /正文

通过阅读mypy问题,似乎调用dict.update()并提供TypedDict不是类型安全的。这对我来说没有意义

我的问题是(特别是第二期,链接如下):

  • 既然一个TypedDict在运行时是一个dict,为什么mypy抱怨不能将一个TypedDict传递到dict.update,说它需要一个Mapping
    • 在我看来,dict就像说Dict[Any, Any],那么为什么不添加一个TypedDict作为dict值呢
    • 在你的回答中,你能提供一个具体的例子吗
  • 一般来说,为什么调用dict.update(SomeTypedDict)不是类型安全的

这方面有两个例子,见mypy期:

  1. python/mypy #6462:TypedDict update()不接受具有兼容子集键的TypedDict

This is a pretty subtle issue. The update call is arguably not type safe.

  1. python/mypy #9086:使用TypedDict更新dict时出现假阳性[arg type]错误

Since TypedDict objects use structural subtyping, there could be additional items not visible through the type Foo, with arbitrary value types. Thus Mapping[str, object] is correct (though it is unintuitive).


示例代码来自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]

Tags: to类型fooistypeanyupdatespam
1条回答
网友
1楼 · 发布于 2024-03-28 10:42:09

PEP 589说:

First, any TypedDict type is consistent with Mapping[str, object]. Second, a TypedDict type A is consistent with TypedDict B if A is structurally compatible with B.

A TypedDict with all int values is not consistent with Mapping[str, int], since there may be additional non-int values not visible through the type, due to structural subtyping. These can be accessed using the values() and items() methods in Mapping, for example.

例如:

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]

相关问题 更多 >