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

Import a module from both within same package and from outside the package in Python 3

提问人:Akronix 提问时间:11/16/2017 最后编辑:Akronix 更新时间:2/16/2023 访问量:11594

问:

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

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

现在,这些是我的条件:

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

因此,在 Python 2 中,我将简单地在 mod2.py 内部执行,并且两者都会按预期工作。import mod1python2 mod2.pypython2 interface.py

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

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

同样,如果我使用相对导入:则有效;但 mod2.py 说:( :(from . import mod1python3 interface.pySystemError: Parent module '' not loaded, cannot perform relative import

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

我很困惑。这种情况在 python 2 中非常简单,但在 python 3 中看起来很尴尬。

更新:我已经上传了这些文件,其中包含(上面称为“解决方案”)工作源代码:https://gitlab.com/Akronix/test_python3_packages。请注意,我仍然不喜欢它,并且看起来比 python2 解决方案丑陋得多。


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

相关链接:

python-3.x 导入

评论

0赞 Akronix 11/16/2017
这是我迄今为止找到的最好的(尽管令人失望)的答案:stackoverflow.com/a/8195271/2904315
3赞 init_js 2/7/2018
在您的 mod2.py 中,您可以使用相对导入,而不是 ,例如 ,或者这样可以减少提及包名称的需要。这些在 python2 和 3 中工作。这里读得很好,相对进口量是十亿次from pkg.mod1 import funfrom .mod1 import funfrom . import mod1

答:

16赞 MisterMiyagi 5/17/2018 #1

顶级域名:

  • 使用 运行代码。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 开关?这是唯一的出路??

如果您的软件包安装正确,您可以从任何地方使用交换机。例如,您始终可以使用 .-mpython3 -m json.tool

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

如果尚未安装包,则可以将 PYTHONPATH 设置为其基目录。这包括搜索路径中的包,并允许交换机正确找到它。-m

如果您位于可执行文件的目录中,则可以执行以快速装载要导入的包。export PYTHONPATH="$(pwd)/.."

我很困惑。这种情况在 python 2 中非常简单,但在 python 3 中看起来很尴尬。

这种情况在 python 2 中基本被打破了。虽然它在许多情况下很简单,但在任何其他情况下都很难或完全不可能解决。

在简单的情况下,新行为更加笨拙,但在任何情况下都是健壮可靠的。

评论

0赞 variable 10/19/2019
我希望你能指导我。案例 3 来自以下链接。作者在案例 3 中说,解决方案 1(使用 -m)是不可能的。如果这是真的,你能告诉我吗?chrisyeh96.github.io/2017/08/08/......
0赞 MisterMiyagi 10/20/2019
@variable 对不起,但很难拼凑出你的意思。乍一看,我会说这不是真的。我建议您在 Stackoverflow.com 上提出一个新问题或访问 python 聊天。
-2赞 Ángel R.G. 2/16/2023 #2

我也有类似的问题。 我解决了它,添加了

import sys 
sys.path.insert(0,".package_name")

添加到包文件夹中的文件中。__init__.py