如何向 UserDict 提供类型提示?

Jea*_* T. 2 python mypy python-typing

我想定义一个UserDict从 JSON 读取值并存储给定键的位置的函数。JSON 文件如下所示:

{
    "pages": [
        {
            "areas": [
                {
                    "name": "My_Name",
                    "x": 179.95495495495493,
                    "y": 117.92792792792793,
                    "height": 15.315315315315303,
                    "width": 125.58558558558553
                },
                ...
              ]
        }
    ]
}
Run Code Online (Sandbox Code Playgroud)

我想向类型 linter(例如 MyPy)表明该字典作为键是字符串,值是Position.

我当前的代码如下:

import json
from collections import UserDict
from dataclasses import dataclass, field
from pathlib import Path
from typing import Dict, List, Optional, Union

from typing_extensions import Literal


JsonPosition = Dict[str, Union[str, float]]
JsonPage = Optional[Dict[Literal["areas"], List[JsonPosition]]]


@dataclass
class Position:
    """Information for a position"""

    name: str
    x: float
    y: float
    width: float
    height: float

    @classmethod
    def from_json(cls, dict_values: JsonPosition):
        return cls(**dict_values)  # type: ignore  # dynamic typing


class Page(UserDict):
    """Information about positions on a page"""

    @classmethod
    def from_json(cls, page: JsonPage):
        """Get positions from JSON Dictionary"""
        if page is None:
            return cls()

        return cls({cast(str, p["name"]): Position.from_json(p) for p in page["areas"]})



JSON = Path("my_positions.json").read_text()
positions = json.loads(JSON)
page_1 = Page.from_json(positions["pages"][0])
Run Code Online (Sandbox Code Playgroud)

我希望 MyPy (或 Pylance 或我使用的任何类型提示)自动识别page_1["My_Name"]Position.

我能改变什么?

Jea*_* T. 5

实际上,您可以像使用 一样直接UserDict使用方括号 ( )提供类型:[...]Dict

class Page(UserDict[str, Position]):
    ...
Run Code Online (Sandbox Code Playgroud)

对于Python 3.6或更早版本,这不起作用。

对于Python >=3.7 和 <3.9,您需要以下内容作为下标collections.UserDict并将其放入特定于类型检查的单独块中(使用常量TYPE_CHECKING):

from __future__ import annotations
from collections import UserDict
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    TypedUserDict = UserDict[str, Position]
else:
    TypedUserDict = UserDict


class Page(TypedUserDict):
    ...
Run Code Online (Sandbox Code Playgroud)

对于Python 3.9+,不需要额外的导入或技巧。