好的,情况很简单。我有这个文件结构:
.
├── 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
都可以正常工作。但是,这是我不太了解的部分,如果我使用
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问题:
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/
最佳答案
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中打破的。虽然在许多情况下很简单,但在任何其他情况下都很难或根本无法修复。
在简单的情况下,新的行为会比较尴尬,但在任何情况下都将是可靠且可靠的。