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