问题描述
好的,场景很简单.我有这个文件结构:
.├── interface.py├── pkg│ ├── __init__.py│ ├── mod1.py│ ├── mod2.py现在,这些是我的条件:
- mod2 需要导入 mod1.
- interface.py 和 mod2 都需要作为主脚本独立运行.如果需要,可以将界面视为实际程序,将 mod2 视为包的内部测试人员.
因此,在 Python 2 中,我只需在 mod2.py 和 python2 mod2.py
和 python2 interface.py
中执行 import mod1
会按预期工作.
但是,这是我不太了解的部分,使用 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'
同样,如果我使用相对导入:来自 .导入 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 问题:
相关链接:
- 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 导入您的代码.导入 mod1
.
我发现的唯一解决方案"是打开一个文件夹并执行 python -m pkg.mod2
然后它就可以工作了.
使用 -m
开关确实是唯一"的解决方案 - 它之前已经是唯一的解决方案.旧的行为只是纯粹靠运气;它甚至可能在不修改您的代码的情况下被破坏.
向上一个文件夹"只会将您的包添加到搜索路径中.安装包或修改搜索路径也可以.详情见下文.
但是我们是否必须在每次导入到该包内的其他模块时添加包前缀 pkg?
你必须有一个对你的包的引用 - 否则你想要哪个模块是不明确的.包引用可以是绝对的,也可以是相对的.
相对导入通常是您想要的.它节省了显式编写 pkg
,从而更容易重构和移动模块.
# 在 mod1.py 中# import mod2 - 这是错误的!它可以拉入任意的 mod2 模块# 这些都是正确的,它们唯一地标识了模块导入 pkg.mod2从 pkg 导入 mod2从 .导入 mod2from .mod2 import foo # 如果 pkg.mod2.foo 存在
请注意,您始终可以使用 as
将您的导入绑定到不同的名称.例如,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 中基本上破坏.虽然在许多情况下很简单,但在其他任何情况下都很难或完全不可能修复.
新行为在简单的情况下更加笨拙,但在任何情况下都是稳健可靠的.
Okay, the scenario is very simple. I have this file structure:
.
├── interface.py
├── pkg
│ ├── __init__.py
│ ├── mod1.py
│ ├── mod2.py
Now, these are my conditions:
- mod2 needs to import mod1.
- both interface.py and mod2 needs to be run independently as a main script. If you want, think interface as the actual program and mod2 as an internal tester of the package.
So, in Python 2 I would simply do import mod1
inside mod2.py and both python2 mod2.py
and python2 interface.py
would work as expected.
However, and this is the part I less understand, using Python 3.5.2, if I do import mod1
; then I can do python3 mod2.py
, but python3 interface.py
throws: ImportError: No module named 'mod1'
:(
So, apparently, python 3 proposes to use import pkg.mod1
to avoid collisions against built-in modules. Ok, If I use that I can do python3 interface.py
; but then I can't python3 mod2.py
because: ImportError: No module named 'pkg'
Similarly, If I use relative import:from . import mod1
then python3 interface.py
works; but mod2.py says SystemError: Parent module '' not loaded, cannot perform relative import
:( :(
The only "solution", I've found is to go up one folder and do python -m pkg.mod2
and then it works. But do we have to be adding the package prefix pkg
to every import to other modules within that package? Even more, to run any scripts inside the package, do I have to remember to go one folder up and use the -m switch? That's the only way to go??
I'm confused. This scenario was pretty straightforward with python 2, but looks awkward in python 3.
UPDATE: I have upload those files with the (referred as "solution" above) working source code here: https://gitlab.com/Akronix/test_python3_packages. Note that I still don't like it, and looks much uglier than the python2 solution.
Related SO questions I've already read:
- 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
Related links:
- 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:
- Run your code with
python -m pkg.mod2
. - Import your code with
from . import mod1
.
Using the -m
switch is indeed the "only" solution - it was already the only solution before. The old behaviour simply only ever worked out of sheer luck; it could be broken without even modifying your code.
Going "one folder up" merely adds your package to the search path. Installing your package or modifying the search path works as well. See below for details.
You must have a reference to your package - otherwise it is ambiguous which module you want. The package reference can be either absolute or relative.
A relative import is usually what you want. It saves writing pkg
explicitly, making it easier to refactor and move modules.
# 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
Note that you can always use <import> as <name>
to bind your import to a different name. For example, import pkg.mod2 as mod2
lets you work with just the module name.
If your package is properly installed, you can use the -m
switch from anywhere. For example, you can always use python3 -m json.tool
.
echo '{"json":"obj"}' | python -m json.tool
If your package is not installed (yet), you can set PYTHONPATH
to its base directory. This includes your package in the search path, and allows the -m
switch to find it properly.
If you are in the executable's directory, you can execute export PYTHONPATH="$(pwd)/.."
to quickly mount the package for import.
This scenario was basically broken in python 2. While it was straightforward in many cases, it was difficult or outright impossible to fix in any other cases.
The new behaviour is more awkward in the straightforward case, but robust and reliable in any case.
这篇关于在 Python 3 中从同一包内和包外导入模块的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!