如何在Python模块中正确使用相对或绝对导入?
在Python中使用相对导入有一个缺点,就是你不能再把模块当作独立的程序来运行了,否则会出现一个错误:
ValueError: 尝试在非包中进行相对导入
代码
# /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)。
6 个回答
放弃相对导入:你应该把你的包命名空间当作全局的来考虑。
让这个想法更容易接受的窍门是适当地编辑 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
你可以用一种稍微不同的方式来“独立运行模块”:
不要这样:
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,而bar又导入了foo。试着在
from foo import bar
中添加到test.py,你会发现它会失败。这个例子需要修改才能正常工作。
所以,你问的其实是希望在相对导入失败时回退到绝对导入;实际上,如果你把foo.py或bar.py当作主模块来执行,其他模块就会在根目录下。如果它们的名字和系统中其他模块相同,那么哪个模块会被选中取决于sys.path中的顺序。因为当前目录通常是第一个,所以如果有本地模块可用,它们会被优先选择——也就是说,如果你在当前工作目录下有一个'os.py'文件,它会被选中,而不是内置的os模块。
一个可能的建议是:
foo.py
try:
from . import bar
except ValueError:
import bar
if __name__ == "__main__":
pass
bar.py:
if __name__ == "__main__":
pass
顺便说一下,从正确的位置调用脚本通常是更好的选择。
python -m foo.bar
这可能是最好的方法。这种方式可以将模块作为脚本运行。