在同一包中进行绝对导入的模块

14

我已将我的导入问题简化为这个简单的基本案例。假设我有一个Python包:

mypkg/
   __init__.py
   a.py
   b.py

a.py 包含以下内容:

def echo(msg):
    return msg
b.py 包含以下内容:
from mypkg import a       # possibility 1, doesn't work
#import a                 # possibility 2, works
#from mypkg.a import echo  # import also fails

print(a.echo())

运行 python b.py 在 Python 2.7.6 和 Python 3.3.5 中都会产生 ImportError: No module named mypkg。我已经尝试在两种情况下添加 from __future__ import absolute_import,但问题仍然存在。

期望的结果:

我希望可能性1能够正常工作。

为什么要这样做:

可能性2不是很理想。假设标准库引入了一个名为 a 的包(在这种情况下不太可能,但你懂的)。虽然 Python 2 会首先搜索当前包,但 Python 3+ 包括绝对导入更改,以便首先检查标准库。 无论出于何种原因,可能性1应该可以正常工作,不是吗?我敢肯定我之前已经做过成千上万次了。

注意:如果您编写一个外部脚本 mypkgfrom mypkg import a 就能正常工作。

我的问题类似于python - absolute import for module in the same directory,但作者暗示我所拥有的应该可以正常工作。


不需要绝对路径到相同的文件夹。请参见引用问题的答案 - llrs
如何在Python Spyder IDE中使用相对导入(相关链接请参见我的答案:它们也适用于绝对导入)how to use relative import within python spyder IDE - jfs
4个回答

22

from mypkg import a是正确的形式。不要从Python包目录内运行脚本,这会使同一模块使用多个名称并可能导致错误。相反,在包含mypkg的目录中运行python -m mypkg.b

为了能够从任何目录运行,mypkg应该在pythonpath中。


如果导入被写成相对导入,例如from . import a,那么python -m mypkg.b也可以正常工作,这对我来说似乎更优雅,因为重命名mypkg不会破坏导入,但是相对导入似乎不受欢迎。你对此有什么看法? - Mr_and_Mrs_D
1
@Mr_and_Mrs_D 的 Python 脚本(__name__ == '__main__')必须使用绝对导入。 - jfs
有道理 - 你能给我提供一个讨论这个问题的参考资料吗? - Mr_and_Mrs_D
1
@Mr_and_Mrs_D:“用作Python应用程序主模块的模块必须始终使用绝对导入。”https://docs.python.org/3.12/tutorial/modules.html#intra-package-references。虽然,在这种情况下不适用:我使用`python -m pkg.script`形式时没有收到“ImportError: attempted relative import with no known parent package”,并且它得到了文档的支持:“在引用包内模块时可以使用此[相对]导入样式。”https://docs.python.org/3.12/library/__main__.html#main-py-in-python-packages - jfs

2
“是的,它不会起作用,因为在您调用 print(mypkg.a.echo()) 的时候,mypkg 还在加载中(mypkg.__init__ -> mypkg.b)。这是因为 Python 优先加载父模块。https://docs.python.org/3/reference/import.html#searching

您可以将 print(mypkg.a.echo()) 包装到一个函数中:


def echo():
   mypkg.a.echo()

然后:
import mypkg.b
mypkg.b.echo()

甚至可以是:

print(sys.modules['mypkg.a'].echo())

您也可以帮助Python找到您的模块:
import importlib
mypkg.a = importlib.import_module('mypkg.a')
mypkg.a.echo()

0

我认为问题出在你在 mypkg 文件夹内没有对 mypkg 的引用。看一下当我尝试运行你的示例时 Python 在做什么(使用详细选项):

# trying /vagrant/mypkg/mypkg.py

这就是为什么它能找到模块,因为它不存在。你可以使用一个小技巧,创建一个名为的文件,并在其中加入以下代码行。
import a

但那只是你在另一件外套中的第二种可能性。不知道你想要实现什么,我会选择Intra-package Reference文本中的第一个例子。我会这样写b.py

from a import echo

print(echo('message'))

-1

试试这个:

import sys
import os
this_dir = os.path.dirname(os.path.realpath(__file__))
sys.path.append(os.path.dirname(this_dir))

from mypkg import a
print(a.echo())

这个能工作,但是不被推荐。如果这是你自己的脚本且不需要分享,那么这没问题。但是你不应该制作会影响用户$PATH环境变量的包。 - Mike Williamson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接