在 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.py
和 python2 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 fun
或 from . 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 中从同一包内和包外导入模块的主要内容,如果未能解决你的问题,请参考以下文章