好的,这个场景非常简单。我有这个文件结构:
.
├── interface.py
├── pkg
│ ├── __init__.py
│ ├── mod1.py
│ ├── mod2.py
现在,这些是我的条件:
因此,在Python 2中,我只需在mod2.py中执行import mod1
,python2 mod2.py
和python2 interface.py
都可以按预期工作。
然而,如果我做import mod1
,这是我不太了解的部分,使用Python 3.5.2;然后我可以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问题:
相关链接:
答案 0 :(得分:2)
TLDR:
python -m pkg.mod2
运行代码。from . import mod1
。唯一的“解决方案”,我发现是上一个文件夹并执行
python -m pkg.mod2
,然后就可以了。
使用-m
开关确实是“唯一”解决方案 - 它已经是以前唯一的解决方案了。旧的行为只是纯粹的运气;它甚至可以在不修改代码的情况下被破坏。
“一个文件夹”只是将您的包添加到搜索路径中。安装包或修改搜索路径也可以。有关详细信息,请参阅下文。
但我们是否必须将包前缀pkg添加到该包中其他模块的每次导入?
您必须拥有对您的包的引用 - 否则您需要的模块不明确。包引用可以是绝对的也可以是相对的。
相对导入通常是您想要的。它明确地节省了写作pkg
,使重构和移动模块变得更容易。
# mod1.py
# import mod2 - this is wrong! It can pull in an arbitrary mod2 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中基本上是已经。虽然在很多情况下它很简单,但在任何其他情况下修复都很困难或完全不可能。
在简单的情况下,新行为更加尴尬,但无论如何都是健壮可靠的。