mypy 设置字典键/接口

mypy set dictionary keys / interface

假设我有一个将字典作为参数的函数:

def f(d: dict) -> None:
    x = d["x"]
    print(x)

我可以指定这个字典 必须 有 mypy 的键 "x" 吗?我正在寻找类似于 interface from typescript 的内容,但没有将 d 更改为 class。

我不想将 d 更改为 class 的原因是因为我正在修改一个大型现有代码库以添加 mypy 类型检查并且使用了这个字典在许多地方。如果我必须将 d["x"] 的所有实例更改为 d.x.

,我将不得不修改大量代码

从 Python 3.8 开始,您可以使用 typing.TypedDict, added as per PEP 589. For older Python versions you can use the typing-extensions package

请注意,PEP 确实承认更好的选择是您对此用例使用 data类,但是:

Dataclasses are a more recent alternative to solve this use case, but there is still a lot of existing code that was written before dataclasses became available, especially in large existing codebases where type hinting and checking has proven to be helpful.

所以更好的答案考虑不同的数据结构,例如命名元组或数据类,您可以在其中指定类型具有的属性。这就是打字稿声明所做的,真的:

The printLabel function has a single parameter that requires that the object passed in has a property called label of type string.

Python 属性在道德上等同于 Typescript 对象属性。 Typescript 对象表示法和 Python 词典有很多共同点可能会混淆问题,但是在尝试将概念映射到 Python 时,您不应将 Typescript 对象声明视为 类。

可能看起来像这样:

from dataclasses import dataclass

@dataclass
class SomeClass:
    x: str

def f(sc: SomeClass) -> None:
    x = sc.x
    print(x)

也就是说,您可以在此处使用 typing.TypedDict

from typing import TypedDict

class SomeDict(TypedDict):
    x: str

def f(d: SomeDict) -> None:
    x = d['x']
    print(x)

TypeDict 声明中的键要么都是必需的,要么都是可选的(当您在声明中设置 total=False 时);您必须使用继承来生成带有一些可选键的类型,请参阅链接的文档。注意 TypedDict currently has issues with a mix of optional and required keys;即使在使用 Python 3.8 时,您可能希望使用 typing-extensions 包来获取 Python 3.9 版本(修复此问题)作为向后移植。只需使用 from typing_extensions import TypedDict 而不是上面的 from typing ... 导入,typing-extensions 包会在适当的时候回退到标准库版本。

Mypy 通过提供 TypedDict 类型扩展了 PEP 484。这允许指定 dict 类型的特定属性。在您的情况下,您可以执行以下操作:

from mypy_extensions import TypedDict

# you can also do HasX = TypedDict('HasX', {'x': str})
class HasX(TypedDict):
    x: str

def f(x: HasX) -> None:
    reveal_type(d["x"])  # error: Revealed type is 'builtins.str'