从Terminal运行脚本时的ModuleNotFoundError

时间:2018-06-07 15:30:40

标签: python python-3.x pycharm

我有以下文件夹结构:

  • app
    • __init__.py
    • utils
      • __init__.py
      • transform.py
    • products
      • __init__.py
      • fish.py

在fish.py中,我导入transform如下:import utils.transform

当我从Pycharm运行fish.py时,它运行得非常好。但是当我从终端运行fish.py时,我收到错误ModuleNotFoundError: No module named 'utils'

我在终端中使用的命令:来自app文件夹python products/fish.py

我已经查看了此处建议的解决方案:Importing files from different folder,将应用程序文件夹的路径添加到sys.path帮助中。但是我想知道是否有任何其他方法使其工作而不在fish.py中添加两行代码。这是因为我在/ products目录中有很多脚本,并且不想在每个脚本中添加2行代码。

我查看了一些开源项目,我看到很多从并行文件夹导入模块的示例,而没有在sys.path中添加任何内容,例如这里: https://github.com/jakubroztocil/httpie/blob/master/httpie/plugins/builtin.py#L5

如何以同样的方式使它适用于我的项目?

2 个答案:

答案 0 :(得分:6)

您可能想要运行python -m products.fish。它与python products/fish.py之间的区别在于前者大致相当于在shell中执行import products.fish(但__name__设置为__main__),而后者没有意识到它在包层次结构中的位置。

答案 1 :(得分:3)

这是@Mad Physicist的回答。


首先,假设app本身是一个程序包(因为您已经向其中添加了__init__.py),并且utilsproducts是其子程序包,则应更改导入import app.utils.transform ,然后从根目录app的父级)运行Python。该答案的其余部分假定您已完成此操作。 (如果您不是打算将app用作根软件包,请在评论中告诉我。)


问题是您正在运行app.products.fish,就好像它是一个脚本一样,即,通过将文件的完整路径提供给python命令:

python app/products/fish.py

这使Python认为此fish.py文件是独立脚本,不属于任何软件包。根据文档中的定义(请参见<script>下的here),这意味着Python将在与脚本相同的目录中搜索模块,即app/products/

如果脚本名称直接指向Python文件,则目录 包含该文件的文件将添加到sys.path的开头,并且该文件 作为__main__模块执行。

但是,当然app文件夹不在app/products/中,因此如果您尝试导入app或任何子包(例如app.utils),它将引发错误。

启动作为软件包一部分的脚本的正确方法是使用 -m(模块)开关reference),它将模块路径作为一个参数并将其作为脚本执行(但将当前工作目录保留为模块搜索路径):

如果给出此选项,则当前目录[...] 将会添加到sys.path的开头。

因此,您应该使用以下代码启动程序:

python -m app.products.fish

现在,当app.products.fish尝试导入app.utils.transform模块时,它将在您当前的工作目录(包含app树)中搜索app/...并成功。 / p>


作为个人建议:请勿将可运行的脚本放入包中。仅使用包来存储所有逻辑和功能(函数,类,常量等),并编写一个单独的脚本以根据需要运行您的应用程序,并将其放在包的外部。这样可以避免出现此类问题(包括the double import trap),并具有一个优点,即您可以为每个程序包编写单独的启动脚本,从而为同一程序包编写多个运行配置。