我来过这里:

http://www.python.org/dev/peps/pep-0328/http://docs.python.org/2/tutorial/modules.html#packagesPython包:相对导入python相对导入示例代码不起作用python 2.5中的相对导入Python中的相对导入Python:禁用相对导入

还有很多我没有复制的URL,有些在SO上,有些在其他网站上,当时我以为我会很快找到解决方案。

永远重复出现的问题是:我如何解决这个“试图在非包中相对导入”消息?

ImportError: attempted relative import with no known parent package

我在pep-0328上创建了一个完全相同的包副本:

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
        moduleY.py
    subpackage2/
        __init__.py
        moduleZ.py
    moduleA.py

导入是从控制台完成的。

我确实在相应的模块中创建了名为垃圾邮件和鸡蛋的函数。很自然,它不起作用。答案显然在我列出的第四个URL中,但对我来说都是校友。我访问的一个URL上有这样的回复:

相对导入使用模块的name属性来确定该模块在包层次结构中的位置。如果模块的名称不包含任何包信息(例如,它被设置为“main”),则无论模块在文件系统上的实际位置如何,都会将相对导入解析为顶级模块。

上面的回答看起来很有希望,但对我来说都是象形文字。所以我的问题是,如何让Python不返回给我“尝试在非包中相对导入”?据推测,有一个答案涉及-m。

有人能告诉我为什么Python会给出这个错误消息,“非包”是什么意思,为什么以及如何定义“包”,以及准确的答案,让幼儿园的学生很容易理解。


当前回答

这确实是python中的一个问题。混淆的根源是人们错误地将相对导入视为路径相对,而不是路径相对。

例如,当您使用faa.py编写时:

from .. import foo

只有当faa.py在执行过程中作为包的一部分被python识别并加载时,这才有意义。在这种情况下,模块的名称例如,faa.py将是some_packagename.faa。如果加载文件只是因为它在当前目录中,那么当运行python时,它的名称将不会引用任何包,最终相对导入将失败。

在当前目录中引用模块的一个简单解决方案是:

if __package__ is None or __package__ == '':
    # uses current directory visibility
    import foo
else:
    # uses current package visibility
    from . import foo

其他回答

以下是一个我不建议使用的解决方案,但在某些情况下可能很有用,因为模块根本没有生成:

import os
import sys
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(parent_dir_name + "/your_dir")
import your_script
your_script.a_function()

@布伦伯恩的回答说明了一切,但如果你像我一样,可能需要一段时间才能理解。这是我的案例,@BrenBarn的答案如何适用于它,也许它会帮助你。

案例

package/
    __init__.py
    subpackage1/
        __init__.py
        moduleX.py
    moduleA.py

使用我们熟悉的示例,并在其中添加moduleX.py对..的相对导入。。模块A。假设我尝试在导入moduleX的subpackage1目录中编写测试脚本,但随后出现了OP描述的可怕错误。

解决方案

将测试脚本移动到与包相同的级别并导入package.subpackage1.moduleX

解释

如前所述,相对导入是相对于当前名称进行的。当我的测试脚本从同一目录导入moduleX时,moduleX中的模块名为moduleX。当遇到相对导入时,解释器无法备份包层次结构,因为它已经位于顶部

当我从上面导入moduleX时,moduleX内部的名称是package.subpackage1.moduleX,可以找到相对的导入

这确实是python中的一个问题。混淆的根源是人们错误地将相对导入视为路径相对,而不是路径相对。

例如,当您使用faa.py编写时:

from .. import foo

只有当faa.py在执行过程中作为包的一部分被python识别并加载时,这才有意义。在这种情况下,模块的名称例如,faa.py将是some_packagename.faa。如果加载文件只是因为它在当前目录中,那么当运行python时,它的名称将不会引用任何包,最终相对导入将失败。

在当前目录中引用模块的一个简单解决方案是:

if __package__ is None or __package__ == '':
    # uses current directory visibility
    import foo
else:
    # uses current package visibility
    from . import foo

根据Lars的建议,我将这种方法包装在一个实验性的新导入库中:ultraimport

它使程序员能够更好地控制导入,并允许基于文件系统的导入。因此,您可以从脚本进行相对导入。父包不是必需的。无论您如何运行代码或当前的工作目录是什么,ultraimport都将始终有效,因为ultraimport使导入变得明确。您不需要更改sys.path,也不需要try/except块来有时执行相对导入,有时执行绝对导入。

然后你可以在somefile.py中写下类似的内容:

import ultraimport
foo = ultraimport('__dir__/foo.py')

__dir__是superimport()的调用程序somefile.py的目录。foo.py将与somefile.py位于同一目录中。

像这样导入脚本时需要注意的一点是,如果它们包含进一步的相对导入。ultraimport有一个内置的预处理器,用于将后续的相对导入重写为ultraimports,以便它们继续工作。尽管如此,由于最初的Python导入是不明确的,所以目前这有点有限,您只能做这么多。

在大多数情况下,当我看到ValueError:试图在顶级包之外进行相对导入并拔出头发时,解决方案如下:

您需要在文件层次结构中更高一级!

#dir/package/module1/foo.py

#dir/package/module2/bar.py
from ..module1 import foo

在dir/package/中启动解释器时导入bar.py将导致错误,尽管导入过程从未超出当前目录。

在dir/中启动解释器时导入bar.py将成功。

同样,对于单元测试:python3-m unittest discover--start directory=。成功地从dir/工作,但不能从dir/package/工作。