在 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.py
,foo.py
和bar.py
我正在寻找适用于 python 2.6+(包括 3.x)的解决方案。
您可以以一种不同的方式开始“独立运行模块”:
代替:
python foo/bar.py
用途:
python -mfoo.bar
当然,
foo/__init__.py
文件必须存在。
另请注意,
foo.py
和 bar.py
之间存在循环依赖 – 这是行不通的。我想这只是你的例子中的一个错误。
更新:看起来使用它作为
foo/bar.py
的第一行也非常有效:
#!/usr/bin/python -mfoo.bar
然后就可以直接在POSIX系统中执行脚本了。
首先,我假设您意识到您所写的内容会导致循环导入问题,因为 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
可能是最好的方法。这将模块作为脚本运行
使其变得可口的技巧是适当地编辑
sys.path
。这里有一些值得深思的地方:
# 上一级目录 _root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) 暂时 sys.path.insert(0, _root_dir)
__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) 会遇到问题。
由于当前的限制,我想知道什么时候有人应该在 python 中使用相对导入。
在我使用的所有配置中,
sys.path
包含当前目录作为第一个参数,因此只需使用
import foo
而不是from . import foo
,因为它会执行相同的操作。