【问题标题】:Is it okay to import a dependency at the point of use rather than at the top of the file?可以在使用点而不是文件顶部导入依赖项吗?
【发布时间】:2020-09-22 18:31:28
【问题描述】:

假设我有一个包含一堆对象的模块,这些对象都不需要第三方库。好吧,几乎没有:一个函数,称为asterix(),需要第三方包。 asterix() 不是模块的重点,但在需要时方便使用。

仅仅为了一个功能就必须要求并导入整个第三方包似乎是一种耻辱。我有哪些选择?将 asterix() 移动到另一个模块/包或 vendoring 看起来很愚蠢。

我发现快速和干净的最佳平衡是简单地在asterix() 内部进行导入,可能需要一些额外的尝试/排除逻辑。它违反了最小意外原则和将导入放在顶部的惯例。但我告诉自己我总是会违反一些东西。

群众智慧对此有何看法?有什么明显的我想念的吗?

def asterix(...):
    try:
        import something_special
    except ModuleNotFoundError as err:
        # do something about err (help the user out!)
    # The special code...

【问题讨论】:

  • 谢谢@wim。是的,PEP8。但对于房间里的成年人来说,这更像是一个问题——当面对这种权衡时,书本之外的技巧是什么?例如,“在每个函数调用时进行导入检查”的缺点可以通过将函数的定义以成功导入为条件来减轻。好的。但伴随着另一套权衡......
  • 您的解决方案不错。每次函数调用时的导入检查只是一个快速的dict 查找,在这里是合理的。
  • @wim 绝对在文件顶部导入是 PEP 8 推荐的做法。但在 PEP 8 中也是这样:“但是,知道何时不一致——有时样式指南建议只是” t 适用。如有疑问,请使用您的最佳判断。查看其他示例并决定什么看起来最好。不要犹豫,问!”

标签: python


【解决方案1】:

当我刚接触该语言时,我也很想使用函数内联导入。事后看来,现在拥有 10 多年的 Python 经验,我可以告诉你,这样做通常不是一个好主意。将所有导入放在模块的顶部,除非您有充分的理由推迟它们。 内联导入隐藏依赖项。如果有充分的理由推迟导入,请确保您有必要的日志记录和监控以了解失败的位置/时间。

我知道"because the style guide says to" 并不是一个真正令人满意的答案,所以这就是我认为主要的潜在问题:Python 中的打包和部署可能很复杂,而且很难做到正确。想象一下,您的代码最终将使用asterix,但由于某种原因没有安装something_special(或者由于任何其他原因导致导入失败,例如缺少递归依赖项)。你想尽早失败。要么在测试套件中崩溃,要么在失败的部署/回滚中。您不希望在运行时出现 ImportError 崩溃,每当应用程序碰巧最终调用 asterix 时(这可能意味着在凌晨 3:00 呼叫了一些糟糕的 on-call)。

我同意供应商代码通常很愚蠢(美化了复制粘贴)。将asterix 移到专用模块中的建议还不错,这实际上是我推荐的方法。

# mod_with_extra_dep.py
import something_special

def asterix():
    ...

要使依赖项成为可选,请使用包元数据:

# setup.py
from setuptools import setup

setup(
    ...
    extras_require={"asterix": ["something_special"]}
)

如果您使用入口点,它们还支持打包元数据中的可选要求(specsetuptools example)。这提供了两全其美:

  • 依赖关系是显式的
  • something_special 除非需要,否则不会实际导入

任何想要使用asterix 的代码都将在访问名称asterix 时触发导入,而不是在实际调用函数时。

【讨论】:

    猜你喜欢
    • 2012-03-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-27
    • 2014-11-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多