Python 2.5中的相对导入

19 投票
2 回答
11620 浏览
提问于 2025-04-17 07:14

我知道关于Python中导入问题的提问很多,但似乎没有人能给出一个清晰的正确用法示例。

假设我们有一个包叫做 mypackage,里面有两个模块 foobar。在 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__',所以你不能进行相对导入。即使mypackagesys.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

撰写回答