Inf*_*ity 7 python python-3.x python-3.6 mypy python-typing
我有以下代码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__)
Run Code Online (Sandbox Code Playgroud)
但是当我在其他文件上使用它时, mypy (正确地)无法识别属性:
import tags
print(tags.A) # Module has no attribute "A"
Run Code Online (Sandbox Code Playgroud)
Python 3.6 有没有可能绕过这个问题?
已知的解决方案(对于我的情况来说不够好):
# type: ignore
每次使用时都使用tags.A
tags.Tags.A
__getitem__
在模块级别使用(仅适用于 Python 3.7)就我个人而言,我要做的就是修改我的导入:
from tags import Tags
Run Code Online (Sandbox Code Playgroud)
..这将使我可以通过在任何地方进行操作来引用枚举项,Tags.A
而无需大惊小怪。
但是,如果您确实想继续从模块命名空间引用这些项目,一种方法是在以下位置定义一个__getattr__
函数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]
Run Code Online (Sandbox Code Playgroud)
在运行时,如果您尝试访问模块对象中未直接定义的某些属性,Python 将尝试调用此函数tags
。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
Run Code Online (Sandbox Code Playgroud)
该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
Run Code Online (Sandbox Code Playgroud)
显然,必须像这样重复两次是相当次优的,但我认为没有一个简单的方法可以解决它。您也许可以通过创建一个自动生成的脚本来缓解这种情况tags.py
,但这也不是最理想的,只是出于不同的原因。
归档时间: |
|
查看次数: |
6422 次 |
最近记录: |