在 Python 3 中从同一包内和包外导入模块

Posted

技术标签:

【中文标题】在 Python 3 中从同一包内和包外导入模块【英文标题】:Import a module from both within same package and from outside the package in Python 3 【发布时间】:2018-04-29 09:17:45 【问题描述】:

好的,场景很简单。我有这个文件结构:

.
├── interface.py
├── pkg
│   ├── __init__.py
│   ├── mod1.py
│   ├── mod2.py

现在,这些是我的条件:

mod2 需要导入 mod1。 interface.py 和 mod2 都需要作为主脚本独立运行。如果您愿意,可以将 interface 视为实际程序,将 mod2 视为包的内部测试人员。

因此,在 Python 2 中,我只需在 mod2.py 中执行 import mod1 即可,python2 mod2.pypython2 interface.py 都会按预期工作。

但是,这是我不太了解的部分,使用 Python 3.5.2,如果我这样做 import mod1;然后我可以做python3 mod2.py,但是python3 interface.py 抛出:ImportError: No module named 'mod1' :(

因此,显然,python 3 建议使用import pkg.mod1 来避免与内置模块发生冲突。好的,如果我使用它,我可以做到python3 interface.py;但后来我不能python3 mod2.py 因为:ImportError: No module named 'pkg'

同样,如果我使用相对导入: from . import mod1 然后 python3 interface.py 工作;但是 mod2.py 说 SystemError: Parent module '' not loaded, cannot perform relative import :( :(

我发现唯一的“解决方案”是上一个文件夹并执行python -m pkg.mod2,然后它就可以工作了。但是我们是否必须将包前缀 pkg 添加到该包中其他模块的每个导入中?更重要的是,要运行包内的任何脚本,我是否必须记住向上一个文件夹并使用 -m 开关?这是唯一的方法吗??

我很困惑。这个场景在 python 2 中非常简单,但在 python 3 中看起来很尴尬。

更新:我已在此处上传带有(称为“解决方案”)工作源代码的这些文件:https://gitlab.com/Akronix/test_python3_packages。注意我还是不喜欢,而且看起来比python2的方案丑多了。


我已经阅读过的相关 SO 问题:

Python -- import the package in a module that is inside the same package How to do relative imports in Python? Absolute import module in same package

相关链接:

https://docs.python.org/3.5/tutorial/modules.html https://www.python.org/dev/peps/pep-0328/ https://www.python.org/dev/peps/pep-0366/

【问题讨论】:

这是迄今为止我找到的最好(虽然令人失望)的答案:***.com/a/8195271/2904315 在您的 mod2.py 中,您可以使用相对导入,而不是 from pkg.mod1 import fun,例如 from .mod1 import funfrom . import mod1,这将减少提及包名称的需要。这些在 python2 和 3 中工作。很好的阅读,relative imports for the billionth time 【参考方案1】:

TLDR:

使用python -m pkg.mod2 运行您的代码。 使用from . import mod1 导入您的代码。

我发现唯一的“解决方案”是上一个文件夹并执行python -m pkg.mod2,然后就可以了。

使用-m 开关确实是“唯一”的解决方案——它之前已经是唯一的解决方案了。旧的行为只是完全靠运气才能奏效。它甚至可以在不修改您的代码的情况下被破坏。

“向上一个文件夹”只会将您的包添加到搜索路径中。安装包或修改搜索路径也可以。详情见下文。

但是我们是否必须将包前缀 pkg 添加到该包中其他模块的每次导入中?

你必须对你的包有一个引用——否则你想要哪个模块是不明确的。包引用可以是绝对的或相对的。

相对导入通常是您想要的。它节省了显式编写pkg,从而更容易重构和移动模块。

# inside mod1.py
# import mod2 - this is wrong! It can pull in an arbitrary mod2 module
# these are correct, they uniquely identify the module
import pkg.mod2
from pkg import mod2
from . import mod2
from .mod2 import foo  # if pkg.mod2.foo exists

请注意,您始终可以使用<import> as <name> 将您的导入绑定到不同的名称。例如,import pkg.mod2 as mod2 允许您仅使用模块名称。

更重要的是,要运行包中的任何脚本,我是否必须记住向上一个文件夹并使用 -m 开关?这是唯一的方法吗??

如果您的软件包安装正确,您可以在任何地方使用-m 开关。例如,您始终可以使用python3 -m json.tool

echo '"json":"obj"' | python -m json.tool

如果您的包尚未安装(还),您可以将PYTHONPATH 设置为其基目录。这会将您的包包含在搜索路径中,并允许-m 开关正确找到它。

如果你在可执行文件的目录下,可以执行export PYTHONPATH="$(pwd)/.."快速挂载包进行导入。

我很困惑。这个场景在 python 2 中非常简单,但在 python 3 中看起来很尴尬。

这种情况在 python 2 中基本上被破坏。虽然在许多情况下它很简单,但在任何其他情况下很难或完全不可能修复。

新的行为在简单的情况下更尴尬,但在任何情况下都健壮可靠。

【讨论】:

我希望你能重新指导我。案例 3 来自以下链接。作者在案例 3 中说,解决方案一(使用 -m)是不可能的。如果这是真的,你能告诉我吗? chrisyeh96.github.io/2017/08/08/… @variable 对不起,很难拼凑出你的意思。乍一看,我会说这不是真的。我建议您在 ***.com 上提出新问题或访问 python 聊天。

以上是关于在 Python 3 中从同一包内和包外导入模块的主要内容,如果未能解决你的问题,请参考以下文章

Python之模块和包

从同一包中的过程返回特定事物的函数[关闭]

定义只能从同一包中的后代访问的方法

python 模块和包

python之路——模块和包

网工学Python——模块和包