构建一个大型python存储库,不导入所有内容

mat*_*att 5 python deployment import

我在使用我们拥有的大型软件仓库来管理导入时遇到了问题.为了清楚起见,让我们假装repo看起来像这样:

repo/
    __init__.py
    utils/
         __init__.py
         math.py
         readers.py             
         ...
    ...
Run Code Online (Sandbox Code Playgroud)

现在我们的__init__.py文件已经设置好,以便我们可以做这样的事情

from repo.utils import IniReader 
Run Code Online (Sandbox Code Playgroud)

在这个例子中repo/utils/__init__.py会有

from .readers import IniReader, DatReader
Run Code Online (Sandbox Code Playgroud)

从可读性的角度来看,这种结构对我们来说效果很好,但我们现在在尝试部署应用程序时遇到了问题.

问题是这个......让我假装我正在编写一个看起来像这样的应用程序:

from repo.utils import IniReader
if __name__ == '__main__':
    r = IniReader('blah.ini')
    print(r.fields)
Run Code Online (Sandbox Code Playgroud)

现在from repo.utils import IniReader将执行repo/utils/__init__.py将在这种情况下将导入IniReader DatReader.让我们假装DatReader看起来像这样:

import numpy as np
import scipy
import tensorflow
from .math import transform

class DatReader():
...
Run Code Online (Sandbox Code Playgroud)

它遵循PEP8,所有导入都在文件的顶部.

这里的问题是DatReader需要一些重量级的导入(例如numpy,scipy,tensorflow是巨大的库).更糟糕的是,from .math import transform可能有类似的东西from repo.contrib import lookup然后点击repo/contrib/__init__.py它开始连锁反应并最终导入我们的整个存储库.

对于我们所有拥有完整开发环境的开发人员而言,这确实不是一个问题,但现在我们正试图(内部)运送应用程序这个导入地狱正成为一个问题.

这个问题有标准解决方案吗?我们已经讨论过只保留__init__.py空,或者只是没有将所有导入放在文件顶部,如PEP8所述.这两种解决方案都有妥协,所以如果有人有建议或参考,我很乐意听到.

谢谢!

Xuk*_*rao 2

暂时退一步看看您似乎面临的基本问题可能会有所帮助,即:“如何处理用户计算机上丢失的 python 包?

基本上这个问题有两类解决方案:

  1. 帮助使缺失的软件包在用户的计算机上可用。
    • 您可以将代码作为分发,用户可以使用pip. 只需在分布式包中包含依赖项规范pip,用户就可以自动下载和安装任何缺少的包。
    • 您可以冻结您的代码,即将您的代码转换为已包含所有必需包的独立应用程序。
  2. 将您的包依赖项分为强制依赖项和可选依赖项,并调整您的代码,以便缺少可选包不会导致所有代码损坏。
    • 正如您已经指出的,您可以清理模块级导入(即__init__.py文件中的导入),这样可选包就不会“过早”加载。在你的情况下,这意味着删除DatReader进口。
    • 正如您也已经指出的,您可以将可选的包导入移动到需要它们的类或函数中。从风格上来说,这并不是真正的最佳选择,但代码本身仍然是完全有效的。通常,每次运行函数时导入语句都会再次执行并不重要,因为实际导入仍然只发生一次
    • 您可以将可选包的导入包装到 try- except 子句中。这将防止发生任何导入错误(当然,当您尝试运行依赖于丢失的包的类或函数时,您仍然会遇到错误)。

try- except 子句中的导入示例:

import warnings
try:
    import scipy
except ImportError:
    warnings.warn("The python package `scipy` could not be imported. As a result "
                  "the class `repo.utils.DatReader` will not be functional.")
Run Code Online (Sandbox Code Playgroud)

现在再次回到您最初的问题“这个问题是否有标准解决方案?”:我会说没有。没有一颗金子弹。所有解决方案都有其自身的优点和缺点,您必须根据您的具体情况决定哪种解决方案是最佳的。