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