【问题标题】:Importing Python modules from different levels of hierarchy从不同层次结构中导入 Python 模块
【发布时间】:2018-11-05 12:10:02
【问题描述】:

在我的 git 存储库的顶层,我有以下文件结构:

miscellaneous Dockerfiles, readme, etc
Code/
    training.py
    data/
        generate.py
        tasksets.py

当我将tasksets 模块作为脚本运行时,有时我想从tasksets 模块中导入generate 模块,因此tasksets 包含以下导入:

import generate

其他时候我想从training 模块中导入tasksets 模块,所以training 包含以下导入:

import tasksets

但是,这个设置给我带来了问题。当我将tasksets 作为脚本运行时,tasksets 可以很好地导入generate,但是如果我在training 中导入tasksets,则当我将training 作为脚本运行时会引发错误(我认为因为training 可以'在默认路径中找不到 generate 作为脚本)。我尝试使用 __init__.py 文件、相对导入等查看各种其他 StackOverflow 问题和答案。目前,我的解决方法是在 tasksets 中使用以下行:

if __name__ == "__main__": import generate
else: from data import generate

但这感觉不对(而且我的 IDE 也不喜欢它)。请有人解释一下如何使用正确的__init__.py 文件分类和导入语句,以便我可以在将tasksets 作为脚本运行时导入generate,并在将training 作为脚本运行时导入tasksets

【问题讨论】:

  • 您不能在data/ 目录中放置一个空的__init__.py。然后你可以在training.py 中写from data import generate,在tasksets.py 中你有from . import generate。这可能是一个解决方案吗?
  • @be-ndee 我刚试过这个。我用from . import generate替换了tasksets.py中的解决方法,在data/目录中添加了空的__init__.py文件,并将tasksets作为脚本运行,得到以下错误:ImportError: cannot import name 'generate'

标签: python python-3.x import module package


【解决方案1】:

你最好使用经典的 Python 模块/包架构。

projectname/
    __init__.py
    __main__.py
    data/
        __init__.py
        generate.py
        tasksets.py

要使用您的应用程序,请进入projectname/../ 目录(上一级projectname/)并运行python -m projectname。这将执行projectname/__main__.py


__main__.py 中你会写如下内容:

from projectname.data import generate
from projectname.data import tasksets

if __name__ == '__main__':
    generate.foo()
    tasksets.bar()
  1. 您将使用绝对导入路径(以您的模块名称和一个点开头,projectname.
  2. 您将从if __name__ == '__main__' 中导入您的子模块
  3. __main__.py 将是您的应用/脚本的唯一入口点。

在任何其他文件中,您将使用相同的语法和路径来导入其他模块:

data/generate.py:

from projectname.data import tasksets

def foo():
    print('SPAM!')
    tasksets.bar()

我不太喜欢的东西,但我不确定是否有任何 PEP 否认它,

在你的projectname/__init__.py 文件中你可以写:

from projectname.data import generate
from projectname.data import tasksets

所以你的两个子模块将被导入你的主范围__init__.py,所以你可以从这个范围导入子模块,比如

data/generate.py:

from projectname import generate

但同样,我并不真正喜欢这种方式(因为显式优于隐式。


最后一点,

  • 你也可以使用python projectname/__main__.py命令,不过我还是推荐python -m projectname
  • 您可以使用setuptools 创建一个setup.py 文件以在您的系统上“安装”您的应用,然后只需运行projectname 命令即可运行它。

【讨论】:

  • 谢谢@Arount 的回答!请您在您的第二个建议中澄清一下,__init__,pys 应该使用 from projectname.data import generatefrom projectname.data import tasksets 中的哪一个? data 文件夹中的那个? (知道在 __init__.py 中添加什么内容也让我在其他解释中感到困惑)
  • 我也同意我想避免* 通配符导入,但是在__init__.py 中放置显式导入的替代版本对我很有吸引力,因为我希望在理想情况下保持我当前的文件结构,因为它非常适合我的项目(据我所知)
  • @JakeLevi 我更新了projectname/__init__.py 部分以使其更加明显。此外,如果您真的想保留当前文件命名,您可以将 __main__.py 重命名为 training.py,但您的目录名称(在您的示例中为 Code)仍将用作包名称(from Code.data import ...)并且您将需要使用python -m Code.training 运行您的代码。我不认为它是最好的,因为它非常令人费解,但它应该运行。
猜你喜欢
  • 1970-01-01
  • 2020-03-16
  • 2013-12-03
  • 2023-02-14
  • 1970-01-01
  • 1970-01-01
  • 2013-02-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多