代码之家  ›  专栏  ›  技术社区  ›  K.Mulier

无法转换为TYPE_CHECKING条件下导入的类

  •  1
  • K.Mulier  · 技术社区  · 3 年前

    1.背景

    我经常只为类型检查器导入python模块,比如 mypy 例如:

    if TYPE_CHECKING:
        import bar
    

    然后我会使用模块 bar 在整个代码中都有类型提示。这些类型提示仅对类型检查器可见,而 酒吧 在实时运行代码时,它本身不会被导入。

    以这种方式给出类型提示效果很好。然而,当试图 铸造 类型,我收到“找不到模块”错误。

    2.示例

    让我们考虑以下示例。我有三个模块/文件:

    • test.py
    • foo.py
    • bar.py

    这是 test.py :

    # FILE: TEST.PY
    # =============
    from typing import *
    import foo
    if TYPE_CHECKING:
        import bar
    
    def my_func():
        foo_obj:foo.Foo = foo.Foo()
        bar_obj:bar.Bar = foo_obj.get_something()
    

    类型检查器 mypy 抱怨最后一行。现在让我们来看看函数 get_something() 在里面 foo.py 找出原因:

    # FILE: FOO.PY
    # =============
    import bar
    
    class Foo:
        def __init__(self) -> None:
            ...
            return
    
        def get_something(self) -> Union[bar.Bar, str]:
            ...
            return something
    

    类型检查器 mypy 知道这一点 get_something() 返回a Bar() -对象或字符串。因此, mypy 抱怨。很公平。

    现在我知道了一个事实,在这种特殊情况下- get_something() 返回a Bar() -对象。类型检查器不必担心。因此,我的第一反应是用断言或强制转换向类型检查器提供提示:

    # ASSERTION:
    # Give hint to type checker with an assertion
    something = foo_obj.get_something()
    assert isinstance(something, bar.Bar)
    bar_obj:bar.Bar = something
    
    # CAST:
    # Give hint to type checker with a cast
    bar_obj:bar.Bar = cast(foo_obj.get_something(), bar.Bar)
    

    在我看来,演员阵容的方法最优雅,因为它可以以最小的开销完成。此外,强制转换没有运行时效果。在运行时 cast() 函数本质上是 nop() 只有类型检查器才能看到演员阵容。

    3.问题

    不幸的是,转换在运行时抛出“找不到模块”错误。这不是我所期望的。毕竟,在运行时应该完全忽略强制转换!我看到了一些处理这个问题的方法,但它们都有其缺点:

    • 导入模块 酒吧 无条件。

      • 赞成的意见: 现在,该模块可以在强制转换中使用,而不会引发错误。
      • 反对: 我必须只导入用于铸造的模块。这感觉不对。这是次优的,有时会导致循环导入。
    • 使用忽略类型检查器的错误 # type: ignore 评论

      • 赞成的意见: 我不需要投。
      • 反对: 逐行关闭类型检查器是最后的解决方案。我更喜欢更干净的东西。

    你知道怎么告诉字体检查器吗 mypy -除了演员阵容或断言之外—— foo_obj.get_something() 返回a Bar() -在这种情况下的对象?或者,是否有一种方法可以在运行时执行强制转换而不会导致代码崩溃(因为缺少模块)?

    1 回复  |  直到 3 年前
        1
  •  4
  •   Silvio Mayolo    3 年前

    一般来说,您可以将类型名称传递给任何 typing 函数为字符串。 mypy 将足够聪明地进行解析,Python运行时不会抱怨,因为它只是一个字符串。所以你可以这样做强制转换。

    bar_obj = cast('bar.Bar', foo_obj.get_something())
    
    推荐文章