调用子模块时出现“没有名为”的Python错误

5

我在使用import语句时遇到了问题,Python报告说找不到名为the_page或the_generator的模块。

task_a.py文件包含import generator.the_page as ThePage语句,当作为主脚本运行时没有问题。

the_generator.py文件包含import tasks.task_a语句,但当我将其作为主脚本运行时,Python会抛出以下错误信息..

Traceback (most recent call last):
  File "/generator/the_generator.py", line 7, in <module>
    import tasks.tasks_a
  File "/generator/tasks/tasks_a.py", line 3, in <module>
    import generator.the_page as ThePage
ImportError: No module named the_page

这是结构。

generator/
    __init__.py
    the_generator.py
    the_page.py
    tasks/
        __init__.py
        task_a.py

也许你们可以帮助我解决问题。感谢你们的所有帮助!

1
不应尝试将脚本从包的中间作为顶级脚本运行。常见问题解答中已经解释了其中的原因,但基本上,主要问题就是:这样会混淆包其他部分的导入。(还有一个事实是,你可能会导致同一模块被导入两次,因为Python无法判断两个不同的名称是否指向同一个模块。) - abarnert
1个回答

10

从一个包的中间运行脚本是个不好的主意,有很多原因,其中最明显的就是你遇到的问题:当你在其他地方import generator.the_generator时,generator变成了一个包,所以导入generator.the_page的绝对或相对路径都能正常工作。但是当你仅仅运行脚本generator/the_generator.py时,就没有generator.the_generator,只有__main__,而且也没有generator包。Python能够找到generator.the_page的惟一方法是它的父目录在sys.path上,但实际上并没有。

你可以猜到,通过修改sys.path来解决这个问题是可行的,但这也是个坏主意。

这种解决方案还存在很多其他问题。最严重的是,很容易导致同一模块被导入两次(因为Python无法知道两个看似不相关的名称恰巧指向同一个模块)。它也不容易部署(如果依赖于位于包内部的脚本,你不能将其安装到/usr/local/bin),如果你的包是从.zip或.egg文件运行的,则无法工作等。


有两种标准方法可以解决这个问题。

首先,以模块而不是脚本的方式运行脚本。从generator的父目录中,使用python -m generator.the_generator代替python generator/the_generator.py

这样做的一个主要优点是,在正常安装部署时,当generator在某个地方的site-packages中时,它与测试一样有效。

或者,创建一个与generator并排的脚本,并运行该脚本而不是其中的模块。这可以简单到将the_generator.py中所有的if __name__ == '__main__':代码移到一个函数中,然后编写一个两行的包装器:

import generator.the_generator
generator.the_generator.main()

同样地,在正常的安装部署中也可以很好地运行。此外,这意味着脚本可以安装到您的bin目录中,使事情变得更加容易,就像pipipython一样。


我认为他们通过PEP 366解决了很多问题,尽管你必须显式地设置__package__ - user2357112
@user2357112:不,实际上并不需要。如果顶层包已经可以使用绝对导入,设置__package__也会使相对导入起作用。(在讨论PEP 366时,明确决定不采取任何措施来简化伪造绝对导入路径的操作,因为这通常是一个坏主意,无论你需要做什么都应该明确和显然地进行黑客攻击。) - abarnert
将脚本作为模块运行非常顺利!python -m generator.the_generator - Hello Human

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