【问题标题】:Python import from parent directory从父目录导入 Python
【发布时间】:2013-11-09 05:28:46
【问题描述】:

我有以下几点:

         ModuleFolder
              |
              |-->. ModuleFile.py .
              |
              '-->. TestsFolder .
                         |
                         '---> UnitTest1.py

我正在尝试从父目录导入。在这种情况下,我尝试从测试文件夹运行“UnitTest1.py”,并从其正上方的目录(文件“ModuleFile.py”)导入。

  • 我知道已经有很多答案了。 SO Question1、SO Question2、Every Other SO Question。我只是找不到“使用../”作为相对导入而不是显式路径。
  • 我知道,从 Python 2.5 开始,他们支持“相对导入”,根据文档中提到使用 from .. import *,但我特意尝试做一个 import MyModuleName,所以我可以在单元测试中更加明确并避免名称混乱/冲突。

我正在做的(并且对我有用)如下:

sys.path.append("../")

然后从父目录导入我需要的东西。

  • 是的,在父目录中有一个__init__.py,
  • 不,我的父路径不是 Python 路径或环境变量的一部分
  • 为什么不直接将父路径添加到sys.path?因为是相对的。如果我从 /home/workspace/MyModule/unittests/ 运行并且我的模块位于 /home/workspace/MyModule/ 下,我假设如果同事运行此路径,则将 /home/workspace/MyModule/ 添加到路径不一定是真的在他自己的 /home/documents/MyModule 目录下的机器上。

我的问题:

这是 Python 专有的吗?如果不是,这有什么问题。有没有更好的办法?或者这真的是一个 RTFM 时刻,答案就在我已经看过的 7 个以上 SO 问题之一中? (我看到那些都推荐显式路径而不是我采用的相对路径方法)。

其他有用信息:

  • Python 2.6
  • 在 Linux 中工作,但也可以轻松跳到 Win。

【问题讨论】:

  • 最好开始使用您的代码作为package 并导入所有内容。相对导入就无关紧要了。

标签: python


【解决方案1】:

不要从测试文件夹运行测试。从项目的根目录运行它,即模块文件夹。您应该很少需要 either sys.path 或 PYTHONPATH,当您这样做时,您要么给其他库造成错误,要么让您的用户生活更加困难。

python -m TestsFolder.UnitTest1

如果您使用像 py.test 这样的测试运行器,您只需从结帐的根目录运行 py.test,它就会为您找到测试。 (假设您将测试命名为更像test_unit1.py。您当前的命名方案有点不正统。;))

【讨论】:

  • “经验丰富的pythonistas”是否理解这一点,因为测试很少从测试目录运行,并且通常应该在模块级别调用测试?当您最好转到测试目录并键入python test.py 以查看结果时,应该从父目录运行测试似乎违反直觉——我的意思是这是对所有其他非测试 *. py 文件。
  • 不,不是。使用python foo/bar.py 运行可导入文件是个坏主意,因为它会将foo/ 添加到sys.path,而这很少是您想要的。使用-m 运行入口点,并使用测试运行器运行测试。 (我很少cd 比我的项目根更深。)
  • 我总是忘记 python -m。需要在我的隔间墙上贴上贴纸之类的东西,因为忘记它已经让我多次花费了几个小时的工作效率。 +1
【解决方案2】:

最好在sys.path的开头插入你的相对路径,像这样:

import sys
sys.path.insert(0, '../')

【讨论】:

  • 不知道为什么你被否决了。但我很感激你的努力(老实说!)。为什么最好放在开头?这不是我第一次看到这个建议,但我一直不明白为什么......
  • @JustinCarroll:您将路径放在开头,以确保即使在路径冲突的情况下也会调用您的代码。
【解决方案3】:

我的建议是:

不要自作聪明,做你应该做的事。 IE。确保您的模块和包位于 Python 路径中的某个位置。

最简单的方法是在用于执行脚本的 shell 中设置环境变量 PYTHONPATH:

$ export PYTHONPATH=/the/directory/where/your/modules/and/packages/are
$ cd /the/directory/where/your/unit/tests/are
$ python test1.py

【讨论】:

  • 我遇到的问题是,当其他人将其拉下来在他们的环境中运行时,他们会在测试中遇到错误(误报),因为他们没有正确设置环境变量。这是他们的问题吗?最好在这里遵循惯例?我很震惊,没有比“每个想要运行 unittest 的用户必须首先设置他们的 python 环境变量,否则他们无法运行它”更好的解决方案......我对此很热心吗? (感谢您的快速回复)
  • 我相信最好遵循约定。确保您的测试快速失败。您通常将所有导入放在单元测试模块文件的顶部。然后单元测试将立即失败,并显示ImportError。告诉你的同事错误的含义以及如何修复它(即 Windows 中的 export PYTHONPATH=... 或 SET PYTHONPATH=...)。
  • 执行此操作的最简单方法是从包含您的包根目录的目录中运行python。
  • 通过添加sys.path.append("../") 是否有任何事情 错误(其中错误== 可能会破坏代码)?我完全明白这是“糟糕的形式”并且违反惯例,并且有更好、更规范的替代方案来解决这个问题——而且,我喜欢这里和@Eevee 提出的建议,但现在我对实际影响感兴趣这样做,以便我可以就利弊向其他人提供建议(超出常规)。
  • 确定:.. 到底在哪里?如果我没有在与您完全相同的地方运行 Python(也许我已经安装了您的库!),它可能在任何地方并且可以拉入任何东西。
【解决方案4】:

使您的测试文件夹成为一个模块(通过添加 __init__.py)。
然后您可以使用 run python -m tests.test_name 或在您的项目主页中使用以下 Makefile:

TEST_FILES = $(wildcard tests/test_*.py)
TESTS = $(subst .py,,$(subst /,.,$(TEST_FILES)))
all.PHONY: test
test:
    @- $(foreach TEST,$(TESTS), \
        echo === Running test: $(TEST); \
        python -m $(TEST); \
        )

【讨论】:

    【解决方案5】:

    您还可以对要导入的模块路径进行符号链接,然后使用该符号链接进行导入。在 python dist-packages 中创建符号链接。

    创建符号链接:

    ln -s "/path/to/ModuleFolder" "/path/to/python/dist/packages/module_symlink_name"

    在脚本中导入模块:

    from module_symlink_name import ModuleFile
    

    无需导出python路径或修改sys路径。

    【讨论】:

      【解决方案6】:

      这总是很令人不安。相对导入是相对于模块结构的。要测试相对导入,您需要将模块导入主文件(从模块外部)。让我尝试展示一个有效的案例:

      $ python use_module.py
      Hello World!
      

      或者您可以将其设为可执行模块并使用python -m 运行它(始终从模块外部)

      $ python -m module.child_a
      Hello World!
      

      结构

      test_module.py
      module
      ├── __init__.py [can be empty]
      ├── child_a
         ├── __init__.py
         ├── __main__.py
      │── child_b
         ├── foo.py
      

      test_module.py

      from module.child_a import message
      print(message)
      

      foo.py

      phrase = "Hello World"
      mark = '!'
      

      child_a/__init__.py

      from .__main__ import message
      

      child_a/__main__.py

      from .. child_b.foo import phrase, mark
      
      message = phrase + mark
      
      if __name__ == "__main__":
          #this block is run when executing `python -m module.etl`
      
          print(message)
      

      注意:如果您在child_a 中添加另一个文件,比如child_a/myscript.py,您仍然可以使用python -m module.sub_a.myscript 执行它(总是从模块外部)

      【讨论】:

        猜你喜欢
        • 2016-12-27
        • 2012-07-08
        • 2020-03-21
        • 2021-08-15
        • 1970-01-01
        • 2015-07-23
        • 2021-10-23
        • 1970-01-01
        • 2021-09-08
        相关资源
        最近更新 更多