我知道 Python 中存在很多关于相同导入问题的问题,但似乎没有人能够提供正确用法的清晰示例。
假设我们有一个包
mypackage
,其中包含两个模块 foo
和 bar
。在 foo
内部,我们需要能够访问 bar
。
因为我们还在开发中,所以
mypackage
不在sys.path
中。
我们希望能够:
mypackage.foo
foo.py
作为脚本运行,并执行 __main__
部分中的示例用法或测试。我们如何在 foo.py 中进行导入,以确保它在所有这些情况下都能工作。
# mypackage/__init__.py
...
# mypackage/foo/__init__.py
...
# mypackage/bar.py
def doBar()
print("doBar")
# mypackage/foo/foo.py
import bar # fails with module not found
import .bar #fails due to ValueError: Attempted relative import in non-package
def doFoo():
print(doBar())
if __name__ == '__main__':
doFoo()
查看来自PEP 328的以下信息:
相对导入使用模块的
属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,将其设置为__name__
),则将解析相对导入,就像该模块是顶级模块一样,无论该模块实际位于文件系统上的位置。'__main__'
当您将
foo.py
作为脚本运行时,该模块的 __name__
是 '__main__'
,因此您无法进行相对导入。即使 mypackage
位于 sys.path
上也是如此。基本上,如果导入了某个模块,则只能从该模块进行相对导入。
以下是解决此问题的几个选项:
1) 在
foo.py
中,检查是否 __name__ == '__main__'
并有条件地将 mypackage
添加到 sys.path
:
if __name__ == '__main__':
import os, sys
# get an absolute path to the directory that contains mypackage
foo_dir = os.path.dirname(os.path.join(os.getcwd(), __file__))
sys.path.append(os.path.normpath(os.path.join(foo_dir, '..', '..')))
from mypackage import bar
else:
from .. import bar
2) 始终使用
bar
导入 from mypackage import bar
,并以 foo.py
自动可见的方式执行 mypackage
:
$ cd <path containing mypackage>
$ python -m mypackage.foo.foo
我的解决方案看起来更干净一些,并且可以与所有其他导入一起放在顶部:
try:
from foo import FooClass
except ModuleNotFoundError:
from .foo import FooClass