就我个人而言,我会修改我的导入方式为:
from tags import Tags
..这将让我能够轻松地通过 Tags.A
在任何地方引用枚举项。
但是,如果您真的想继续从模块命名空间引用这些项,一种方法是在 tags.py
中定义一个 __getattr__
函数:
class Tags(enum.Flag):
NONE = 0
A = enum.auto()
B = enum.auto()
C = enum.auto()
def __getattr__(name: str) -> Tags:
return Tags[name]
在运行时,如果您尝试访问一些未直接定义在
tags
模块对象中的属性,则Python将尝试调用此函数。Mypy理解这个约定,并假设如果定义了
__getattr__
,则该模块是“不完整的”。因此,它会将返回类型用作您尝试访问的任何缺少的属性的类型。
但是,您可能仍然希望执行
globals().update(Tags.__members__)
,主要是为了优化性能,以跳过在运行时实际调用
__getattr__
函数的步骤。
不过,如果tags.py只包含一个枚举,则此策略只适用于一个枚举。否则,您需要将返回类型设置为类似于
Union[Tags, MyOtherEnum]
(难以操作),甚至只是
Any
(这样会失去使用类型检查器的好处)。
此策略还意味着mypy将无法通过考虑实际枚举值来进行更复杂的类型推断和缩小范围。不过,这主要只与使用Literal enums有关。
如果这些是问题,您可能需要采用更加强制的方法,如下所示:
from typing import TYPE_CHECKING
class Tags(enum.Flag):
NONE = 0
A = enum.auto()
B = enum.auto()
C = enum.auto()
globals().update(Tags.__members__)
if TYPE_CHECKING:
NONE = Tags.NONE
A = Tags.A
B = Tags.B
C = Tags.C
TYPE_CHECKING
常量在运行时始终为false,但类型检查器认为它始终为true。
但是,如果您要花费直接告诉mypy这些变量的每一个变体的费用,那么您可能会跳过尝试自动更新全局变量并改为执行以下操作:
class Tags(enum.Flag):
NONE = 0
A = enum.auto()
B = enum.auto()
C = enum.auto()
NONE = Tags.NONE
A = Tags.A
B = Tags.B
C = Tags.C
显然,像这样重复两次是相当不理想的,但我认为没有简单的解决方法。也许你可以通过创建一个自动生成
tags.py
脚本来减轻这种情况,但由于不同的原因,这也是相当不理想的。