构建大型python存储库,以避免导入所有内容

构建大型python存储库,以避免导入所有内容,python,deployment,import,Python,Deployment,Import,我遇到了一个问题,我们有一个大型软件回购管理进口。为了清楚起见,让我们假设回购协议看起来像这样: repo/ __init__.py utils/ __init__.py math.py readers.py ... ... from repo.utils import IniReader if __name__ == '__main__': r = IniRead

我遇到了一个问题,我们有一个大型软件回购管理进口。为了清楚起见,让我们假设回购协议看起来像这样:

repo/
    __init__.py
    utils/
         __init__.py
         math.py
         readers.py             
         ...
    ...
from repo.utils import IniReader
if __name__ == '__main__':
    r = IniReader('blah.ini')
    print(r.fields)
import numpy as np
import scipy
import tensorflow
from .math import transform

class DatReader():
...
现在我们的
\uuuu init\uuuuu.py
文件已经设置好,这样我们就可以做类似的事情了

from repo.utils import IniReader 
在本例中,
repo/utils/__init__.py

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

问题是。。。让我们假设我正在编写一个如下所示的应用程序:

repo/
    __init__.py
    utils/
         __init__.py
         math.py
         readers.py             
         ...
    ...
from repo.utils import IniReader
if __name__ == '__main__':
    r = IniReader('blah.ini')
    print(r.fields)
import numpy as np
import scipy
import tensorflow
from .math import transform

class DatReader():
...
现在,repo.utils导入IniReader的
将执行
repo/utils/\uuuu init\uuuuuuuuuuuupy
,在这种情况下,它将导入
IniReader
DatReader
。让我们假设
DatReader
看起来像这样:

repo/
    __init__.py
    utils/
         __init__.py
         math.py
         readers.py             
         ...
    ...
from repo.utils import IniReader
if __name__ == '__main__':
    r = IniReader('blah.ini')
    print(r.fields)
import numpy as np
import scipy
import tensorflow
from .math import transform

class DatReader():
...
它遵循PEP8,所有导入都位于文件的顶部

这里的问题是,
DatReader
需要一些重量级导入(例如numpy、scipy、tensorflow都是大型库)。更糟糕的是,
from.math import转换
可能有类似于
from repo.contrib import lookup
的内容,然后点击
repo/contrib/uuu init_uuuuuuuuu.py
,这会引发连锁反应,最终导入整个存储库

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

这个问题有标准的解决方案吗?我们已经讨论过保持
\uuuu init\uuuu.py
为空,或者像PEP8所说的那样,不让所有导入都位于文件的顶部。这两种解决方案都有折衷之处,所以如果有人有建议或参考资料,我很乐意听听


谢谢

退一步看一下您似乎面临的基本问题可能会有所帮助,即:“如何处理用户机器上缺少的python包?”

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

  • 帮助使丢失的软件包在用户的计算机上可用。
    • 您可以将代码分发为用户可以使用
      pip
      安装的文件。只需包含在您的分布式软件包中,
      pip
      将为用户提供自动下载和安装任何缺少的软件包的功能
    • 您可以修改您的代码,即将代码转换为已包含所有必需包的独立应用程序
  • 将包依赖项分为强制依赖项和可选依赖项,并调整代码,以使缺少可选包不会导致所有代码都中断。
    • 正如您已经指出的,您可以清理模块级导入(即
      \uuuu init\uuuuu.py
      文件中的导入),以便不“过早”加载可选包。在您的情况下,这意味着删除
      DatReader
      导入
    • 正如您已经注意到的,您可以将可选的包导入移动到需要它们的类或函数中。就风格而言,这并不是真正的最优,但代码本身仍然是完全有效的。通常情况下,每次运行函数时都会再次执行import语句并不重要,因为
    • 您可以将可选包的导入包装到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.")
    
    现在再回到你最初的问题“这个问题有没有一个标准的解决方案?”:我会说没有,没有一个金子弹。所有的解决方案都有各自的优点和缺点,你必须决定哪种解决方案最适合你的具体情况