【问题标题】:Python typing - is there a way to avoid importing of optional type if it's None?Python类型 - 如果它是None,有没有办法避免导入可选类型?
【发布时间】:2020-06-23 05:10:10
【问题描述】:

假设我们有一个这样的函数定义:

def f(*, model: Optional[Type[pydantic.BaseModel]] = None)

所以该函数不需要安装pydantic,直到您将某些内容作为model 传递。现在假设我们要将函数打包到 pypi 包中。我的问题是,是否有办法避免将pydantic 引入包依赖项,只是为了进行类型检查?

【问题讨论】:

  • 您不必导入完整的模块。可以导入具体型号from pydantic import BaseModel
  • 要考虑的选项是使用字符串类型名称,例如Optional[Type["pydantic.BaseModel"]]。然后,您的代码将在不安装 pydantic 模块的情况下正常运行。它仍然可以用于静态类型检查。

标签: python pypi typing mypy


【解决方案1】:

Python 的 typing 模块可以使用字符串类型名称以及类型本身,就像您在示例中所做的那样。

如果您不希望在代码运行时评估类型,并且在模块尚未导入时引发异常,那么您可能更喜欢使用字符串名称。您的代码 sn-p 将是:

def f(*, model: Optional[Type["pydantic.BaseModel"]] = None)

您使用 mypy 的静态类型检查应该会继续工作,但您的包将不再需要依赖于 pydantic

正如您在docs 中发现的那样,python 的typing 模块包含一种方法来指定某些导入仅对(字符串文字)类型注释是必需的:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    import pydantic

这将仅在TYPE_CHECKINGTrue 时执行import pydantic 语句,主要是在使用mypy 分析代码中的类型时。正常运行程序时,不执行import语句。因此,该选项只能与字符串文字类型提示一起使用;否则,缺少导入会导致程序正常执行失败。

由于类型提示的惰性求值将在 Python 4.0 中实现,

from __future__ import annotations

可用于在 Python 3 中启用此行为。使用此导入语句,不需要字符串文字类型。

【讨论】:

  • > 仅用于(字符串文字)类型注释您实际上并不局限于这种方式的字符串文字
  • 当然不是,但文档说:“如果仅前向引用(字符串文字)或 cmets 中的类型注释需要导入”。如果您以原始形式使用类型,则 TYPE_CHECKING 没有用处,例如pydantic.BaseModel 因为导入将始终需要执行您的代码。
  • 我还发现了一个奇怪的 flake8 反应,它只将"pydantic.BaseModel" 作为字符串传递。如果您想更改或包含它:gitlab.com/pycqa/flake8/-/issues/621
  • @imbolc 你的链接好像失效了,能查一下吗?
  • 我明白了,他们可能必须批准我的问题或其他内容,将其复制到一个要点中:gist.github.com/imbolc/e47632323cd53f4c26514864ecc38460
【解决方案2】:

我会做类似的事情

try:
    from pydantic import BaseModel as PydanticBaseModel
except ImportError:
    PydanticBaseModel = None

我不认为它比 dspencer proposed 更好,但有时一些奇怪的样式指南可能会禁止你使用字符串类型名称,所以我只想指出另一个解决方案。

【讨论】:

    【解决方案3】:

    我尝试关注dspenser's advice,但我发现mypy 仍然给我Name 'pydantic' is not defined 错误。然后我发现了这个chapter in the docs,它似乎也适用于我的情况:

    from typing import TYPE_CHECKING
    
    if TYPE_CHECKING:
        import pydantic
    

    您可以在__future__.annotations (python 3.8.1) 中使用普通类(而不是字符串文字):

    from __future__ import annotations
    
    from typing import TYPE_CHECKING, Optional, Type
    
    if TYPE_CHECKING:
        import pydantic
    
    
    def f(*, model: Optional[Type[pydantic.BaseModel]] = None):
        pass
    

    如果由于某种原因您不能使用__future__.annotations,例如你在 python dspenser's solution 的字符串文字输入。

    【讨论】:

    • 是的,在我的方法下,mypy 会抱怨缺少导入,但您的代码会成功执行。
    • @dspencer 我支持你,但这确实应该合并到你的答案中。
    • @JLPeyret 感谢您的建议 - 我已按照您的建议将其合并到我的答案中。
    • 文档的“最新”版本中缺少该章节。请参阅“稳定”版本:mypy.readthedocs.io/en/stable/common_issues.html#import-cycles
    猜你喜欢
    • 1970-01-01
    • 2022-06-16
    • 2017-07-15
    • 2021-11-25
    • 1970-01-01
    • 2014-05-24
    • 2015-10-08
    • 1970-01-01
    • 2016-07-20
    相关资源
    最近更新 更多