我有以下代码
try:
from mypackage.optional.xxx import f1, f2
except ImportError:
from mypackage.optional.yyy import f1, f2
[xxx
和yyy
模块提供相同的功能,但是功能的编码非常不同,并且基于不同的外部库(它们是我的程序包的可选依赖项),接受不同的输入类型。
不幸的是mypy
在抱怨:
error: Incompatible import of "f1" (imported name has type "Callable[[Arg(Any, 'yyyarg1')], Any]", local name has type "Callable[[Arg(Any, 'xxxarg1')], Any]")
我该如何解决这个问题?有条件地导入相同功能(即具有相似签名的相同功能名称)的最佳方法是什么?
这里的问题是mypy对这两种导入有些挑剔-在满足mypy之前,两个库需要具有相同的API。
这包括任何参数名称,因为关键字参数是一件很重要的事情:执行f1(xxxarg1=blah)
对于第一个导入有效,但对后者无效。
(此特定情况的解决方法是(a)使您的参数具有相同的名称,(b)使用仅在python 3.8+中可用的positional-only arguments,或(c)给您的参数名称加上两个下划线前缀这是将参数声明为仅位置的mypy特定方式-但此策略适用于所有Python版本。)
就我个人而言,我认为使两个函数的签名相同是最好的选择,因为它有助于最大程度地减少代码中存在细微错误的可能性/减少所需的测试量。
但是如果将您的API修改为相同是不可行的,则可以抑制该错误,也可以尝试使mypy通过以下方式使您的导入类型更精确地进行检查:
typing.TYPE_CHECKING
变量,在运行时始终为False,但被mypy视为始终为true--always-true/--always-false
命令行标志,可以让mypy假定某些变量始终为true或false。总共,您知道可以采用三种不同的方法:
方法1:在第二次导入时抑制任何错误
[首先,如果两个库具有几乎相同的API,并且您不关心两者之间的任何细微差别,则一种策略可能是仅忽略后者的导入,这将使mypy抑制源自该最终版本的任何错误。行。
对所有其他行的类型检查将不受影响,这意味着mypy将继续假定--always-true/--always-false
和f1
是从xxx导入的。
f2
此类型忽略选项可能是最实用的方法。
方法2:明确选择第一个导入,而忽略第二个
或者,如果您不喜欢忽略任何内容,则可以通过执行以下操作使mypy完全忽略该导入:
try: from mypackage.optional.xxx import f1, f2 except ImportError: from mypackage.optional.yyy import f1, f2 # type: ignore
执行
from typing import TYPE_CHECKING if TYPE_CHECKING: # Ignored at runtime, but not by mypy from mypackage.optional.xxx import f1, f2 else: # Ignored by mypy, but not at runtime try: from mypackage.optional.xxx import f1, f2 except ImportError: from mypackage.optional.yyy import f1, f2
也可以,尽管它使代码更加隐秘。
要注意的重要一点是,此方法和忽略类型的方法在类型安全性/不安全性方面完全相同。如果您想更清楚地了解自己在做什么或不惜一切代价避免忽略,则可以选择这种方法。
方法3:对两个变量进行类型检查
第三个也是最后一个选项是运行mypy两次,每个库使用if False: ... else: ...
标志运行一次。这将是最安全的类型和最严格的选项。
例如,您可以做:
--always-true/--always-false
...然后同时运行
from typing import TYPE_CHECKING # Actual runtime logic if not TYPE_CHECKING: # Ignored by mypy, but not at runtime try: from mypackage.optional.xxx import f1, f2 USES_XXX = True except ImportError: from mypackage.optional.yyy import f1, f2 USES_XXX = False # For the benefit of mypy if TYPE_CHECKING: if USES_XXX: from mypackage.optional.xxx import f1, f2 else: from mypackage.optional.yyy import f1, f2
和mypy --always-true=USES_XXX your_code
。