我在 a.py
上有以下代码:
class Tags(enum.Flag):
NONE = 0
A = enum.auto()
B = enum.auto()
C = enum.auto()
# Allow using tags.A instead of tags.Tags.A
globals().update(Tags.__members__)
但是当我在其他文件上使用它时,mypy(理所当然地)无法识别属性:
import tags
print(tags.A) # Module has no attribute "A"
在 Python 3.6 中有没有可能绕过这个的方法?
已知的解决方案(对我来说还不够好):
# type: ignore
每次使用 tags.A
tags.Tags.A
__getitem__
在模块级别(仅适用于 Python 3.7)最佳答案
就个人而言,我会做的是修改我的进口做:
from tags import Tags
..这会让我通过做
Tags.A
来引用枚举项无处不在,大惊小怪。但是如果你真的想继续从模块命名空间中引用这些项目,一种方法是定义一个
__getattr__
function内tags.py
: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 将无法通过考虑实际枚举值来进行更复杂的类型推断和缩小。不过,这主要仅在您使用文字枚举时才相关。
如果有这些问题,您可能不得不采用更暴力的方法,如下所示:
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
常量在运行时总是假的,但被类型检查器认为总是真。但是,如果您打算不惜代价直接将这些变体告诉 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
对你来说,但这也是相当不理想的,只是出于不同的原因。
关于python - 在动态属性设置上绕过 mypy 的 "Module has no attribute",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60739889/