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

时间:2010-09-01 10:11:39

标签: 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

我应该如何修改示例代码才能执行所有代码:test.pyfoo.pybar.py

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

6 个答案:

答案 0 :(得分:22)

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

而不是:

python foo/bar.py

使用:

python -mfoo.bar

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

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

更新:使用它作为foo/bar.py的第一行似乎也很有效:

#!/usr/bin/python -mfoo.bar

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

答案 1 :(得分:16)

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

from foo import bar

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

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

可能的建议是:

foo.py

try:
    from . import bar
except ValueError:
    import bar

if __name__ == "__main__":
    pass

bar.py:

if __name__ == "__main__":
    pass

顺便说一句,从正确位置调用脚本通常方式更好。

python -m foo.bar

可能是最好的方式。这runs the module as a script

答案 2 :(得分:1)

Ditch相对导入:无论如何,您应该将您的包命名空间视为全局命名空间。

让这个变得可口的诀窍是适当地编辑sys.path。这是一些值得深思的问题:

# one directory up
_root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.insert(0, _root_dir)for now

答案 3 :(得分:1)

每个文件夹中都需要__init__.py

相对导入仅适用于:

python test.py

test.py导入foo.py和foo.py可以相对导入test.py及以上文件夹中的任何内容。

你做不到:

cd foo
python foo.py
python bar.py

它永远不会奏效。

您可以尝试sys.path.append或sys.path.insert解决方案,但是你会搞砸路径,你会遇到f = open(filename)的问题。

答案 4 :(得分:0)

为什么不把“main”放在另一个.py文件中?

答案 5 :(得分:-2)

到目前为止,我找到的唯一解决方案是根本不使用相对导入。

由于目前的限制,我想知道有人应该在python中使用相对导入。

在我使用的所有配置中,sys.path包含当前目录作为第一个参数,因此只需使用import foo而不是from . import foo,因为它也会这样做。

相关问题