如何使用相对路径导入脚本?

How to import script with relative path?

我有以下目录结构

   project/
        bin/
            script
        stuff/
            __init__.py
            mainfile.py

在脚本内部,我有以下命令来启用命令行执行:

#!/usr/bin/env python
from stuff import mainfile

这行得通,但我原以为需要跳上一级...

from ..stuff import mainfile

from project.stuff import mainfile

我在这里错过了什么?

您需要先将父目录添加到sys.path。尝试以下操作:

# This is a file in bin/ directory.
this_file_path = os.path.dirname(__file__)
sys.path.append(os.path.join(this_file_path, '..'))
import stuff.mainfile

实际上 none 您的示例应该开箱即用。

让我们稍微修改一下bin/script.py

#! /usr/bin/env python

import sys
print sys.path

from stuff import mainfile

这应该会产生类似于

的结果
['.../project/bin', ...]
Traceback (most recent call last):
  File "bin/script.py", line 6, in <module>
    from stuff import mainfile
ImportError: No module named stuff

只有the directory of the script(不是当前目录)自动添加到sys.path

As initialized upon program startup, the first item of this list, path[0], is the directory containing the script that was used to invoke the Python interpreter.* If the script directory is not available (e.g. if the interpreter is invoked interactively or if the script is read from standard input), path[0] is the empty string, which directs Python to search modules in the current directory first. Notice that the script directory is inserted before the entries inserted as a result of PYTHONPATH.

因此 sys.path 上没有 stuff 模块。我不确定您的环境设置,但这是没有设置其他参数时的规范结果(例如 PYTHONPATH)。

同样,

from ..stuff import mainfile

会产生经典ValueError: Attempted relative import in non-package。您被告知您只能相对于实际模块进行相对导入。由于内部 script .. 不引用模块(因为脚本本身可以说是顶级模块),相对导入在这里不起作用。从 Python 的角度来说,脚本之上没有模块,因此 .. 在脚本的上下文中使用时不指代有形的东西。

请注意,这也意味着它 有助于仅通过删除 __init__.py 标记将 projectproject/bin 放入模块本身文件。相对导入到脚本的 parent 只有当脚本的父级实际上是 python 有一个概念的东西时才有可能。

这就是 -m 命令行开关存在的原因之一,使得从命令行 运行 模块成为可能。例如,给定上面的相对 import

python -m project.bin.script

可以解决问题,但前提是从正确的目录 (projectdir/..) 执行。

这个问题更严重
from project.stuff import mainfile

因为 project 目录仅在您从 project 上面的目录启动脚本时自动位于 sys.path 不指定运行:

的主脚本
cd <projectdir>/..
python -m project.bin.script
# works
cd <projectdir>
python -m bin.script
# does not work, because `sys.path` starts with <projectdir>
# and it's `stuff.mainfile` now, not `project.stuff.mainfile`.

如果您想在脚本中从 project 导入模块,请根据您的需要修正 sys.path

import sys
import os

sys.path.insert(0, os.path.dirname(sys.path[0]))
from stuff import mainfile