Python 使用 dict.update 如果传入 TypedDict 则不是类型安全的?

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

从通读

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

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

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

mypy
问题中找到了两个这样的例子:

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

这是一个非常微妙的问题。更新调用可以说不是类型安全的。

  1. python/mypy #9086:使用 TypedDict 更新字典时出现误报 [arg-type] 错误

由于 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]
python dictionary python-typing mypy
1个回答
3
投票

PEP 589 说:

首先,任何

TypedDict
类型都与
Mapping[str, object]
一致。其次,如果 A 在结构上与 B 兼容,则
TypedDict
类型 A 与
TypedDict
B 一致。

包含所有 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]
    
© www.soinside.com 2019 - 2024. All rights reserved.