从终端运行脚本时出现 ModuleNotFoundError

Posted

技术标签:

【中文标题】从终端运行脚本时出现 ModuleNotFoundError【英文标题】:ModuleNotFoundError when running script from Terminal 【发布时间】:2018-11-17 14:19:47 【问题描述】:

我有以下文件夹结构:

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'

我在终端中使用的命令:来自应用文件夹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

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

【问题讨论】:

通过查看设置来检查你是否在 Pycharm 中使用虚拟环境 @NicolòGasparini 我认为在 Pycharm 中使用 virtualnev:在 Project Interpreter 中我选择了 /application/venv/bin/python。我也在终端中使用相同的 virtualnev... 你能显示你的__init__.py文件在哪里吗? 还展示了如何从命令行运行模块。我猜 PyCharm 在设置路径方面非常聪明。 看看这个***.com/questions/1054271/…,它应该可以满足您的需求,但是从长远来看,如果您的 utils 文件夹增长,您可能需要确保它位于路径中,这样您就不必做相对进口。不过你可能会遇到这种情况:***.com/questions/30669474/… 【参考方案1】:

您可能想运行python -m products.fish。这和python products/fish.py 的区别在于前者大致相当于在shell 中执行import products.fish(但将__name__ 设置为__main__),而后者不知道它在包层次结构中的位置.

【讨论】:

感谢 Mad,我刚刚尝试运行 python -m products.fish,它有效!【参考方案2】:

这扩展了@Mad Physicist 的回答。


首先,假设app 本身是一个包(因为您在其中添加了__init__.py)并且utilsproducts 是它的子包,您应该将导入更改为import app.utils.transform,并从根目录运行 Pythonapp 的父级)。这个答案的其余部分假设你已经这样做了。 (如果您不打算将 app 设为根包,请在评论中告诉我。)


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

python app/products/fish.py

这使得 Python 认为这个 fish.py 文件是一个独立的脚本,它不属于任何包的一部分。正如文档中所定义的(参见here,在<script> 下),这意味着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 并成功。


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

【讨论】:

以上是关于从终端运行脚本时出现 ModuleNotFoundError的主要内容,如果未能解决你的问题,请参考以下文章

从终端运行时出现运行时错误,但不是从 Eclipse

从用户终端运行 Eclipse 时出现错误,但不是从 root 运行

从终端运行 npm 时出现“module.js:557 throw err”

从终端运行时出现“java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver”错误

在 VSCode 中运行任何 Python 脚本时出现“&”语法错误?

从 shell 脚本运行 python 文件时出现 ModuleNotFoundError 错误