【问题标题】:Typing an unimported module键入未导入的模块
【发布时间】:2022-10-20 15:58:51
【问题描述】:

我有一个 Python 包,它具有可选的 [extras] 依赖项,但我想坚持在所有方法上键入。

情况是在我的文件中,我有这个

class MyClass:

    def __init__(self, datastore: Datastore):  # <- Datastore is azureml.core.Datastore
        ...

    def my_func(self):
        from azureml.core import Datastore
        ...

我从函数内部导入,因为在不使用附加功能时应导入同一文件中的其他类(附加功能为azureml)。

所以这显然失败了,因为我在导入之前引用了Datastore。从__init__ 方法中删除Datastore 类型显然可以解决问题。

所以总的来说,我的问题是在输入可选(附加)包时是否可以使用打字,如果可以,如何使用。

请注意,在类定义中导入(在class MyClass 语句下方)不是有效的解决方案,因为在导入模块时会调用此代码

【问题讨论】:

    标签: python annotations type-hinting python-typing


    【解决方案1】:

    您可以使用TYPE_CHECKING

    第 3 方静态类型假定为 True 的特殊常量 跳棋。它在运行时为 False。

    运行时为 False: 所以它不会影响你的模块的行为。

    from typing import TYPE_CHECKING
    
    if TYPE_CHECKING:
        from azureml.core import Datastore
    
    
    class MyClass:
        def __init__(self, datastore: Datastore):
            ...
    
        def my_func(self):
            from azureml.core import Datastore
            ...
    

    由于我想实际展示这一点,我将使用 operator.itemgetter 作为实例,因为它可以被类型检查器识别,但 azureml.core 不是:

    from typing import TYPE_CHECKING
    
    if TYPE_CHECKING:
        from operator import itemgetter
    
    
    class MyClass:
        def __init__(self, datastore: itemgetter):
            ...
    
        def my_func(self):
            from operator import itemgetter
            ...
    
    
    obj1 = MyClass(itemgetter(1))  # line 16
    obj2 = MyClass(10)             # line 17
    

    这是 Mypy 错误:

    main.py:17: error: Argument 1 to "MyClass" has incompatible type "int"; expected "itemgetter[Any]"
    Found 1 error in 1 file (checked 1 source file)
    

    这表明它作为例外工作。

    【讨论】:

      【解决方案2】:

      只需添加我的两分钱:

      虽然这当然是一个解决方案,但我认为使用 TYPE_CHECKING 常量是项目结构的一个危险信号。它通常(尽管并非总是)显示存在循环依赖或关注点分离不佳。

      在您的情况下,它似乎是后者,正如您所说:

      我从函数内部导入,因为在不使用附加功能时应导入同一文件中的其他类

      如果MyClass 提供可选的功能,它应该绝对驻留在自己的模块中,并且不是与其他提供核心功能的类一起。

      当您将MyClass 放入其自己的模块(例如my_class)时,您可以将其依赖项与所有其他导入一起放在顶部。然后,您将来自my_class 的导入放在一个处理加载内部可选依赖项的逻辑的函数中。

      除了可见性和可以说更好的风格之外,这种设置相对于您提供的设置的一个优点是my_class 模块本身将是一致的,并且导入失败,如果缺少额外的 azureml 依赖项(或损坏/重命名/弃用),而不是仅在调用 MyClass.my_func 时在运行时。

      您会惊讶地发现意外忘记安装所有额外的依赖项是多么容易(即使在生产环境中也是如此)。然后你会感谢星星,当代码立即透明地失败时,而不是在稍后的某个时间在运行时。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-09-12
        • 2021-12-08
        • 1970-01-01
        • 2015-10-06
        • 1970-01-01
        • 2018-01-20
        相关资源
        最近更新 更多