如何在Python中进行相对导入?

620 投票
17 回答
417016 浏览
提问于 2025-04-11 09:16

想象一下这个文件夹结构:

app/
   __init__.py
   sub1/
      __init__.py
      mod1.py
   sub2/
      __init__.py
      mod2.py

我正在编写 mod1,需要从 mod2 导入一些东西。我该怎么做呢?

我试过 from ..sub2 import mod2,但是出现了“在非包中尝试相对导入”的错误。

我在网上查了一下,只找到一些关于"sys.path 操作"的技巧。难道没有更简单的方法吗?


我所有的 __init__.py 文件现在都是空的。

我这样做是因为 sub2 包含了一些在子包(比如 sub1subX 等)之间共享的类。

我想要的行为和 PEP 366 中描述的一样(感谢 John B)。

17 个回答

132
  1. 你运行 python main.py
  2. main.py 会做: import app.package_a.module_a
  3. module_a.py 会做 import app.package_b.module_b
main.py
setup.py
app/ ->
    __init__.py
    package_a/ ->
       __init__.py
       module_a.py
    package_b/ ->
       __init__.py
       module_b.py

另外,第2步或第3步也可以用: from app.package_a import module_a

只要你的 PYTHONPATH 里有 app,这就能正常工作。这样的话,main.py 可以放在任何地方。

所以你写了一个 setup.py,用来把整个应用程序包和子包复制(安装)到目标系统的 Python 文件夹里,同时把 main.py 复制到目标系统的脚本文件夹里。

165

这是我找到的一个解决办法:

我使用相对导入的方式,比如 from ..sub2 import mod2。然后,如果我想运行 mod1.py,我就去 app 的上级目录,然后用 python -m app.sub1.mod1 来运行这个模块。

这个问题发生的真正原因是,相对导入是通过模块的 __name__ 属性来工作的。如果模块是直接运行的,那么 __name__ 就会被设置为 __main__,这时它并不包含任何关于包结构的信息。所以,Python 就会报出 relative import in non-package 的错误。

通过使用 -m 选项,你给 Python 提供了包结构的信息,这样它就能成功解析相对导入了。

我在做相对导入时遇到过很多次这个问题。看了很多之前的回答后,我还是没能找到一个干净的解决办法,而不需要在所有文件中添加冗余的代码。(不过有些评论真的很有帮助,感谢 @ncoghlan 和 @XiongChiamiov)

希望这能帮助到正在为相对导入问题苦恼的人,因为阅读 PEP 真的不太有趣。

384

问题在于,你是通过将 mod1.py 作为参数传递给解释器来运行这个模块的,这样它就被当作 '__main__' 来执行了。

根据 PEP 328 的说明:

相对导入是通过模块的 __name__ 属性来判断这个模块在包中的位置。如果模块的名字没有包含任何包的信息(比如它被设置为 '__main__'),那么相对导入就会被当作这个模块是一个顶级模块来处理,不管这个模块实际上在文件系统中的位置在哪里。

在 Python 2.6 中,他们增加了可以相对于主模块来引用模块的功能。PEP 366 详细描述了这个变化。

撰写回答