Python 2.5中的相对导入
我知道关于Python中导入问题的提问很多,但似乎没有人能给出一个清晰的正确用法示例。
假设我们有一个包叫做 mypackage
,里面有两个模块 foo
和 bar
。在 foo
模块中,我们需要访问 bar
模块。
因为我们还在开发这个包,所以 mypackage
还不在 sys.path
中。
我们希望能够:
- 导入
mypackage.foo
- 将
foo.py
作为脚本运行,并执行__main__
部分的示例用法或测试。 - 使用Python 2.5
那么我们在 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()
2 个回答
7
我的解决方案看起来更简洁,可以放在最上面,和其他的导入一起:
try:
from foo import FooClass
except ModuleNotFoundError:
from .foo import FooClass
32
看看下面这段来自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) 始终使用from mypackage import bar
来导入bar
,并以一种方式执行foo.py
,使得mypackage
自动可见:
$ cd <path containing mypackage>
$ python -m mypackage.foo.foo