事实上 - 如果您的枚举是动态构建的,那么它是“数据”而不是代码。 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))