【问题标题】:Importing module from a directory above current working directory从当前工作目录上方的目录导入模块
【发布时间】:2021-01-14 08:57:17
【问题描述】:

首先,stackoverflow 上有很多关于此的解决方案,但从我尝试过的解决方案中,它们都不起作用。我正在使用远程机器(linux)。我正在使用 ipython 解释器在 dir-2/module_2.py 文件中进行原型设计。此外,我试图避免使用绝对路径,因为这台远程机器中的绝对路径又长又丑,我希望我的代码在下载后能在其他机器上运行。

我的目录结构如下:

/project-dir/
            -/dir-1/
                  -/__ init__.py
                  -/module_1.py
            -/dir-2/
                  -/__ init__.py
                  -/module_2.py
                  -/module_3.py

现在我想从module_2 导入module_1。然而这个stackoverflow帖子中提到的解决方案:link of using

sys.path.append('../..')
import module_2

不起作用。我收到错误:ModuleNotFoundError: No module named 'module_1'

此外,在 ipython 解释器中,import .module_3 内的 module_2 之类的东西会引发错误:

import .module_3
       ^ SyntaxError: invalid syntax

点运算符不应该在同一个目录中工作吗?总的来说,我对导入机制感到很困惑。非常感谢您对最初问题的任何帮助!非常感谢!

【问题讨论】:

    标签: python import python-import


    【解决方案1】:

    为什么它不起作用?

    如果您运行module1.py 文件并且想要导入module2,那么您需要类似

    sys.path.append("../dir-2")
    

    如果你在里面使用sys.path.append("../..") then the folder you added to the path is the folder containing project-dirand there is notmodule2.py`文件。


    语法import .module_3 用于相对导入。如果您尝试执行 module2.py 并且它包含 import .module_3 它不起作用,因为您正在使用 module2.py 作为脚本。要使用相对导入,您需要将 module2.pymodule_3.py 都视为模块。也就是说,其他一些文件导入 module2 和 module2 使用此语法从 module3 导入一些东西。

    关于如何进行的建议

    解决这两个问题的一种可能的解决方案是组织项目的属性和(可选地,一个好主意)打包您的库(即,使您的代码“可安装”)。然后,一旦安装了您的库(在您正在工作的虚拟环境中),您就不需要 hacky sys.path 解决方案。您将能够从任何文件夹导入您的库。

    此外,不要将您的模块视为脚本(不要运行您的模块)。使用单独的 python 文件作为您的“可执行文件”(或入口点)并从那里导入您需要的所有内容。这样,module*.py 文件中的相对导入将正常工作,您不会感到困惑。

    可能的目录结构可以是

    /project-dir/
                - apps/
                      - main.py
                - yourlib/
                      -/__ init__.py
                      -/dir-1/
                            -/__ init__.py
                            -/module_1.py
                      -/dir-2/
                            -/__ init__.py
                            -/module_2.py
                            -/module_3.py
    
    

    请注意,yourlib 文件夹以及子文件夹包含一个 __init__.py 文件。使用这种结构,您只需运行main.py(名称不必是main.py)。

    案例 1:你不想打包你的库

    如果您不想打包您的库,那么您可以在 main.py 中添加 sys.path.append("../") 以将“project-dir/ 文件夹添加到路径中。这样您的 yourlib 库将在main.py。你可以做类似from yourlib import module_2的事情,它会正常工作(并且module_2可以使用相对导入)。或者,你也可以直接将main.py放在project-dir/文件夹中,你不需要完全更改 sys.path,因为在这种情况下,project-dir/ 将是“工作目录”。

    请注意,您还可以在 project-dir 中拥有一个 tests 文件夹,并且要运行测试文件,您可以执行与运行 main.py 相同的操作。

    案例 2:你想打包你的库

    之前的解决方案已经解决了您的问题,但加倍努力会增加一些好处,例如依赖关系管理,无论您身在何处都无需更改sys.path。打包库有多种选择,由于其简单性,我将使用poetry 展示其中一种选择。

    安装诗歌后,你可以在终端中运行下面的命令来创建一个新项目

    poetry new mylib
    

    这将创建以下文件夹结构

    mylib/
         - README.rst
         - mylib/
                - __init__.py
         - pyproject.toml
         - tests
    

    然后,您可以根据需要添加apps 文件夹以及mylib/ 中的子文件夹(每个子文件夹都有一个__init__.py 文件)。

    pyproject.toml 文件指定依赖项和项目元数据。您可以手动编辑它和/或使用诗歌添加新的依赖项,例如

    poetry add pandas
    poetry add --dev mypy
    

    例如,添加pandas 作为依赖项和mypy 作为开发依赖项。之后就可以运行了

    poetry build
    

    创建一个虚拟环境并在其中安装您的库。您可以使用poetry shell 激活虚拟环境,您将能够从任何地方导入您的库。请注意,您可以更改库文件,而无需再次运行 poetry build

    最后,如果你想在 PyPi 中发布你的库供大家查看,你可以使用

    poetry publish --username your_pypi_username --password _passowrd_
    

    TL;博士

    使用井井有条的项目结构,为您执行的脚本提供清晰的位置。特别是,如果您执行的脚本位于您的模块所在的文件夹之外,则效果会更好。另外,不要将模块作为脚本运行(否则不能使用相对导入)。

    【讨论】:

    • 感谢您提供如此详尽的回答。希望我能投票两次。我只是在寻找这样的东西。事实上,我正在研究如何打包我的库。无论如何,我实际上需要在不久的将来打包我的库,我现在就去做。
    • 顺便说一句,既然你已经知识渊博了,你能告诉我是否可以使用诗歌制作 pip 可安装包吗?我需要将我的算法打包为任何人都可以安装的 pip。如果您提到我应该首先从哪里开始寻找,这将帮助我不要随意寻找所有地方!再次感谢!
    • pip 从PyPi 安装库,这意味着我描述的过程就是你所需要的。是否使用诗歌、是否使用 distutils 手动编写 setup.py 或是否使用此处未提及的任何其他可用工具都无关紧要。您将能够使用 pip install yourlibrary 从 Pypi 安装您的库。我已经以这种方式发布了两个库。
    猜你喜欢
    • 2020-11-03
    • 2012-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-21
    • 2020-08-31
    • 2015-07-23
    • 2023-03-14
    相关资源
    最近更新 更多