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

oxf4rvwz  于 2023-02-20  发布在  Python
关注(0)|答案(2)|浏览(105)

好的,场景很简单。我有这样的文件结构:

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

现在,我的条件是:

  • mod2需要导入mod1。
  • www.example.com和mod2都需要作为主脚本独立运行。2如果你愿意,可以把interface当作实际的程序,mod2当作包的内部测试器。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.

因此,在Python 2中,我只需要在www.example.com中执行import mod1python2 mod2.pypython2 interface.py都会按预期工作。mod2.py and both python2 mod2.py and python2 interface.py would work as expected.
然而,这是我不太理解的部分,使用Python 3.5.2,如果我使用import mod1;我就可以执行python3 mod2.py但是python3 interface.py会抛出ImportError: No module named 'mod1':(
所以,很显然,python 3建议使用import pkg.mod1来避免与内置模块的冲突。但是我不能python3 mod2.py因为ImportError: No module named 'pkg'
类似地,如果我使用相对导入:from . import mod1python3 interface.py工作;但是网站上说SystemError: Parent module '' not loaded, cannot perform relative import:(:(mod2.py says SystemError: Parent module '' not loaded, cannot perform relative import :( :(
我发现唯一的"解决方案"是打开一个文件夹并执行python -m pkg.mod2,然后它就可以工作了。但是我们必须在该包中的其他模块的每个导入中添加包前缀pkg吗?更重要的是,要运行包中的任何脚本,我必须记住打开一个文件夹并使用-m开关吗?这是唯一的方法吗?
我很困惑,这个场景在python2中非常简单,但在python3中看起来很笨拙。
更新:我已经上传这些文件与(称为"解决方案"以上)工作的源代码在这里:https://gitlab.com/Akronix/test_python3_packages。注意,我仍然不喜欢它,而且看起来比python2解决方案难看得多。
我已经阅读过的相关SO问题:

相关链接:

llew8vvj

llew8vvj1#

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开关吗?这是唯一的方法吗?
如果您的软件包安装正确,您可以从 * anywhere * 使用-m开关。例如,您可以始终使用python3 -m json.tool

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

如果你的软件包还没有安装,你可以把PYTHONPATH设置为它的基目录,这样你的软件包就包含在搜索路径中,这样-m开关就可以正确地找到它。
如果您位于可执行文件的目录中,则可以执行export PYTHONPATH="$(pwd)/.."来快速挂载要导入的包。
我很困惑,这个场景在python2中非常简单,但在python3中看起来很笨拙。
这种情况在python2中基本上是"坏掉的",虽然在许多情况下很简单,但在其他情况下很难或根本不可能修复。
新的行为在直接的情况下更笨拙,但在任何情况下都是健壮和可靠的。

wdebmtf2

wdebmtf22#

我也遇到过类似的问题。我通过添加

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

到软件包文件夹中的__init__.py文件中。

相关问题