我来过这里:

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会给出这个错误消息,“非包”是什么意思,为什么以及如何定义“包”,以及准确的答案,让幼儿园的学生很容易理解。


当前回答

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

为PyPi编写了一个小python包,可能有助于了解这个问题。如果希望能够从包/项目中运行包含包含高级包的导入的python文件,而不直接在导入文件的目录中,则该包可以作为变通方法。https://pypi.org/project/import-anywhere/

其他回答

这确实是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

在大多数情况下,当我看到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/工作。

因此,在与许多其他人一起对此吹毛求疵之后,我在本文中看到了Dorian B发布的一篇笔记,它解决了我在开发用于web服务的模块和类时遇到的具体问题,但我也希望能够在编写代码时使用PyCharm中的调试器工具对它们进行测试。要在自包含的类中运行测试,我将在类文件的末尾包含以下内容:

if __name__ == '__main__':
   # run test code here...

但是如果我想在同一个文件夹中导入其他类或模块,那么我就必须将所有的导入语句从相对表示法更改为本地引用(即删除点(.))。但是在阅读了Dorian的建议后,我尝试了他的“一行”,结果成功了!我现在可以在PyCharm中进行测试,当我在另一个被测试的类中使用该类时,或者当我在web服务中使用它时,我可以将测试代码留在原地!

# import any site-lib modules first, then...
import sys
parent_module = sys.modules['.'.join(__name__.split('.')[:-1]) or '__main__']
if __name__ == '__main__' or parent_module.__name__ == '__main__':
    from codex import Codex # these are in same folder as module under test!
    from dblogger import DbLogger
else:
    from .codex import Codex
    from .dblogger import DbLogger

if语句检查我们是否将此模块作为main运行,或者它是否正在另一个作为main测试的模块中使用。也许这是显而易见的,但我在这里提供了这条注释,以防其他人对上述相关进口问题感到沮丧,可以利用它。

__name__根据所讨论的代码是在全局命名空间中运行还是作为导入模块的一部分运行而改变。

如果代码未在全局空间中运行,__name__将是模块的名称。如果它在全局命名空间中运行——例如,如果您将其键入控制台,或使用python.exe yourscriptnamehere.py将模块作为脚本运行,那么__name__将变为“__main__”。

如果__name__=='__main__'用于测试代码是否从全局命名空间运行,您将看到许多python代码,这允许您拥有一个兼作脚本的模块。

您是否尝试从控制台进行这些导入?

一门外语有太多太长的答案。所以我会尽量缩短。

如果你写的是。导入模块,与您所想的相反,模块不会从当前目录导入,而是从包的顶层导入!如果将.py文件作为脚本运行,它根本不知道顶层在哪里,因此拒绝工作。

如果从上面的包目录中像这样启动py-m package.module,那么python知道顶层在哪里