指定Python字典中的mypy键

12

假设我有一些代码如下:

def get_x(d: dict) -> int:
    d["x"]

然而,我希望告诉mypy,d应该只包含特定的键(例如只有“x”键)。这样,如果我在代码中较低的位置犯了一个错误,试图引用d的无效键,mypy将触发错误。

我的问题是:

  1. 这是否可行?能否使用mypy验证字典键?
  2. 如果可以,如何实现?如果不行,是否有更好的解决方案?

类似于这个吗? - PieCot
3
我认为 TypedDict 是你要找的。 - Michael0x2a
2个回答

12
在尝试将一些类型信息应用于AWS API Gateway/Lambda集成时,我也遇到了这个问题。
正如评论中@Michael0x2a所指出的那样,TypedDict似乎是解决问题的方法,特别是因为它不需要在传递给函数之前进行任何转换(就像你提到的API响应要求的那样)。
from mypy_extensions import TypedDict

DictWithOnlyX = TypedDict('DictWithOnlyX', {"x": str})

# error: Extra key 'y' for TypedDict "DictWithOnlyX@58"
dx1: DictWithOnlyX = { "y": 123 }

# error: Incompatible types (expression has type "int",
#        TypedDict item "x" has type "str")
dx2: DictWithOnlyX = { "x": 123 }

# GOOD!
dx3: DictWithOnlyX = { "x": "123" }

应该注意的是,如果在生产环境和开发/测试环境之间分离依赖项,那么这将使mypy成为一个生产依赖项。

1
有几种方法可以做到这一点,假设您的数据结构必须具有相同的属性,但它们不使用字典。您可以使用namedtuple而不是传入字典。使用namedtuple,您可以将其视为对象,但不能添加新字段。
from collections import namedtuple

A = namedtuple("A", ["x", "y", "z"])

def get_x(d: A) -> int:
    d.x

另一种方法是创建一个类并使用__slot__属性。这也可以防止意外插入新属性。
class A:
    __slot__ = ["x", "y", "z"]

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

使用上述方法,您必须在开始时定义字段。

1
我正在测试API,这些API返回JSON。我不想将每个JSON转换为namedtuple。我希望在客户端方法返回的内容方面进行一些类型检查。有没有办法缩小字典类型的范围? - Daniel Kats
不,我不这么认为。Python是一种解释型语言。您可以创建一个函数来检查现有键与一组键是否匹配,并查看是否存在任何不应该存在的键。 - drum
1
我想在类型层面上做这件事 - 我希望mypy能够发现这个错误。我已经有了运行时检查,但那不是同一回事。 - Daniel Kats

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接