返回字典值源自泛型基类的类型提示

0 投票
1 回答
51 浏览
提问于 2025-04-14 17:13

背景

我正在写一个系统,用来解析一个包含不同部分的配置文件。每个部分可以有一系列条目,每个条目都有不同的逻辑来解析配置文件中的数据。我把每个部分封装成一个类(这些类都是从一个基础部分类派生的)。对于每个部分类,我还有一个配套的解析器类,它充当该特定部分的工厂。所以每个解析器会接收加载的配置文件,解析它的部分,并返回相应的部分类类型。

为了让用户能够在文件中添加更多部分或覆盖基础部分,我需要返回一个包含所有基础解析器的表。这个想法是,用户可以为他们的自定义部分添加更多解析器,或者通过从中派生并根据需要修改解析代码来覆盖特定的解析器。这也可以包括从原始部分返回一个派生类。为此,我需要返回一个字典,将每个解析器的名称映射到相应的解析器。现在我遇到的问题是,如何正确地为这个函数添加类型提示。

最小示例

下面是我想做的一个简化示例。首先是部分类:

@dataclass
class BaseSection:
    extra: int = 0

@dataclass
class ASection(BaseSection):
    a: int = 0

@dataclass
class BSection(BaseSection):
    b: int = 0

现在是解析器:

T = TypeVar("T", bound=BaseSection)

class BaseSectionParser(Generic[T]):
    def parse(self, data: str) -> Optional[T]:
        if len(data) == 0:
            return None

        return self._get_data(data)

    @abstractmethod
    def _get_data(self, data: str) -> T:
        pass

class ASectionParser(BaseSectionParser[ASection]):
    def _get_data(self, data: str) -> ASection:
        ret = ASection()
        if data == "a":
            ret.a = 1
        return ret

class BSectionParser(BaseSectionParser[BSection]):
    def _get_data(self, data: str) -> BSection:
        ret = BSection()
        if data == "b":
            ret.b = 2
        return ret

最后,这是返回字典的函数:

def get_parsers() -> dict: # <- What do I put here for a type hint?
    return {
            "a": ASectionParser,
            "b": BSectionParser,
            }

我尝试过的事情

到目前为止,我尝试了以下类型提示:

  • dictdict[str, Any]dict[str, type]:这些是最简单的选项。Mypy没有报错,但显然它们没有提供关于返回的字典是什么的信息。我知道这些可以用,但我在想是否有更好的方式来添加类型提示。
  • dict[str, BaseSectionParser]:Mypy对这两行给出了相同的错误 error: Dict entry 0 has incompatible type "str": "type[ASectionParser]"; expected "str": "BaseSectionParser[Any]"
  • dict[str, BaseSectionParser[T]]:Mypy也给出了错误,唯一的区别是它把Any参数改成了T

老实说,我对该怎么做感到困惑。我该如何为这个函数添加类型提示,以传达它返回的是“一个字符串到从BaseSectionParser派生的类型的字典”这一概念呢?

编辑 1

扩展了尝试的类型提示,从dict到包括dict[str, type]dict[str, Any]

1 个回答

2

这个怎么样呢?

def get_parsers() -> dict[str, type[ASectionParser] | type[BSectionParser]]:
    return {
        "a": ASectionParser,
        "b": BSectionParser,
    }

或者...

def get_parsers() -> dict[str, type[BaseSectionParser]]:
    return {
        "a": ASectionParser,
        "b": BSectionParser,
    }

这两种选择都会给你正确的类型提示,并且不会出现任何错误。

撰写回答