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

How to properly use relative or absolute imports in Python modules?

在python中使用相对导入有一个缺点,您将不能再以标准方式运行模块,因为您将得到一个异常:ValueError: Attempted relative import in non-package

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# /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

我正在寻找一个能与Python2.6+协同工作的解决方案(包括3.x)。


您可以用一种不同的方式开始"以标准方式运行模块":

而不是:

1
python foo/bar.py

用途:

1
python -mfoo.bar

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

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

更新:将其用作foo/bar.py的第一行似乎也非常有效:

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

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


首先,我假设您认识到您所写的将导致循环导入问题,因为foo导入bar和viceversa;尝试添加

1
from foo import bar

去测试.py,你会发现它失败了。必须更改示例才能工作。

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

一个可能的建议是:

P.Py

1
2
3
4
5
6
7
try:
    from . import bar
except ValueError:
    import bar

if __name__ =="__main__":
    pass

bar.py:

1
2
if __name__ =="__main__":
    pass

从适当的位置调用脚本通常会更好。

1
python -m foo.bar

可能是最好的方式。这将以脚本的形式运行模块。


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

相对导入仅在执行以下操作时有效:

1
python test.py

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

你不能这样做:

1
2
3
cd foo
python foo.py
python bar.py

它永远不会起作用。

您可以尝试sys.path.append或sys.path.insert解决方案,但是您会弄乱路径,并且您会遇到f=open(文件名)的问题。


丢弃相对导入:无论如何,您应该将包名称空间视为全局名称空间。

要使这一点更受欢迎的诀窍是适当地编辑sys.path。这里有一些思考的食物:

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


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


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

由于当前的限制,我想知道什么时候应该有人在Python中使用相对导入。

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