如何在Python模块中正确使用相对或绝对导入?

sor*_*rin 26 python module packages python-module python-import

在Python中使用相对导入有一个缺点,您将无法再将模块作为独立模块运行,因为您将获得异常: ValueError: Attempted relative import in non-package

# /test.py: just a sample file importing foo module
import foo
...

# /foo/foo.py:
from . import bar
...
if __name__ == "__main__":
   pass

# /foo/bar.py: a submodule of foo, used by foo.py
from . import foo
...
if __name__ == "__main__":
   pass
Run Code Online (Sandbox Code Playgroud)

我应该如何修改样本代码,以便能够执行所有:test.py,foo.pybar.py

我正在寻找一个适用于python 2.6+(包括3.x)的解决方案.

Jac*_*zny 22

你可以用一种不同的方式开始'以独立方式运行模块':

代替:

python foo/bar.py
Run Code Online (Sandbox Code Playgroud)

使用:

python -mfoo.bar
Run Code Online (Sandbox Code Playgroud)

当然,foo/__init__.py文件必须存在.

还请注意,您foo.py和之间存在循环依赖关系bar.py- 这不起作用.我想这只是你的例子中的一个错误.

更新:似乎它也可以很好地使用它作为第一行foo/bar.py:

#!/usr/bin/python -mfoo.bar
Run Code Online (Sandbox Code Playgroud)

然后,您可以直接在POSIX系统中执行脚本.

  • +1,这正是"执行独立"模块**作为软件包**的一部分的正确方法(如果你想使用相对导入,你必须这样做). (15认同)
  • 这是一个丑陋的黑客,我认为是不可接受的,特别是因为它更难以执行它们(特别是在Windows上) (3认同)

Ala*_*oni 16

首先,我假设你意识到你所写的内容会导致循环导入问题,因为foo导入bar而反之; 尝试添加

from foo import bar
Run Code Online (Sandbox Code Playgroud)

到test.py,你会发现它失败了.必须更改示例才能工作.

所以,当相对导入失败时,你所要求的实际上是回归到绝对导入; 事实上,如果您正在执行foo.py或bar.py作为主模块,其他模块将只位于根级别,如果他们与系统上的另一个模块共享该名称将选择哪个模块依赖于sys.path中的顺序.由于当前目录通常是第一个,因此如果可用,将选择本地模块 - 即,如果您在当前工作目录中有一个'os.py'文件,它将被选中而不是内置模块.

一个可能的建议是:

foo.py

try:
    from . import bar
except ValueError:
    import bar

if __name__ == "__main__":
    pass
Run Code Online (Sandbox Code Playgroud)

bar.py:

if __name__ == "__main__":
    pass
Run Code Online (Sandbox Code Playgroud)

通过从适当位置调用脚本的方式是通常的方式更好.

python -m foo.bar
Run Code Online (Sandbox Code Playgroud)

可能是最好的方式.这将模块作为脚本运行.