如何为动态创建的枚举添加类型提示?

2024-06-25

考虑一下我正在创建一个Enum类如下,

from enum import Enum

key_value = {"DAY": "d", "WEEK": "w", "MONTH": "m"}
FooEnum = Enum("FooEnum", key_value)

怎样才能使FooEnum类代表正确的类型提示(以识别其属性,例如DAY, WEEK and MONTH)?

Note: 实际情况下,大小key_value会有点大 - 比如说 100 个条目。


事实上 - 如果您的枚举是动态构建的,那么它是“数据”而不是代码。 Python 中的静态类型检查处理什么code。如果您在代码中编写“myvar.DAY”,并且 DAY 是由作为数据获取的字符串创建的枚举成员,则 Python 静态检查无法应对它:它们来自“不同的宇宙”。

最直接的做法就是忽略为动态创建的枚举硬编码成员名称的行上的类型提示。

(有趣的事实是,您在尝试时可能会碰到:mypy 将在代码片段中的 Enum 调用上出错:它只需要输入文字字典inline在对 Enum 的调用中,并且将拒绝内省变量 断言它是一本字典。换句话说,该行FooEnum = Enum("FooEnum", key_value)使 mypy 错误为“错误:Enum() 需要字符串、元组、列表或字典文字作为第二个参数”)

因此,困境已定:mypy 进行静态代码检查。以某种方式操纵动态创建协议(协议的子类)typing.Protocol)从动态数据中删除并将您的枚举注册为它的实现也无法工作 - mypy 将无法静态检查协议的有效性。

除了用 Enum 标记所有使用 Enum 的行之外,还有什么可能# type: ignore正如我上面所说,正在创建一个带有枚举成员的静态子集的协议 - 并在“.py”文件中至少硬编码代码中显式使用的成员。

在“一切照旧”中,mypy 会自动识别处理协议的类,无需进行任何更改。这里的情况并非如此:mypy 无论如何都不知道枚举里面有什么,所以我们需要显式调用register协议上的方法来指示实现它的每个枚举。现在,还有另一个问题:由于 Enum 值及其类之间的“混杂”关系,您不能只将 Enum 子类注册为实现协议并期望它能够工作 - mypy 将检查您的枚举的实例,并将“看到”它的类被传递,以及错误。

因此,不仅需要对代码中实际使用的属性子集进行硬编码,还需要显式强制转换 (typing.cast) 在调用将使用 Enum 的地方之前对协议类进行枚举。

import typing as t
import json
from enum import Enum

key_value: dict = {"DAY": "d", "WEEK": "w", "MONTH": "m"}
DateEnum = Enum("DateEnum", key_value) # type: ignore
# ^  you can't escape from this "ignore" anyway.
TimeStampEnum = Enum("TimeStampEnum", json.load(open("file_with_time_units.json"))) # type: ignore


class MinimalTimeUnits(t.Protocol):
    # we only care about using these in code:
    DAY: str
    MONTH: str


def blah(val: MinimalTimeUnits)-> None:
    # Code uses just the enumeration members defined in the procotol:
    x = f"{val.DAY} of {val.MONTH}"


some_condition = True  # <- here so mypy won't error on missing variable on this example

def somecode()->None:
    ...
    time_enum = DateEnum if some_condition else TimeStampEnum
    blah(t.cast(MinimalTimeUnits, time_enum))
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何为动态创建的枚举添加类型提示? 的相关文章

随机推荐