从终端运行脚本时出现 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
)并且utils
和products
是它的子包,您应该将导入更改为import app.utils.transform
,并从根目录运行 Python(app
的父级)。这个答案的其余部分假设你已经这样做了。 (如果您不打算将 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 时出现错误,但不是从 root 运行
从终端运行 npm 时出现“module.js:557 throw err”
从终端运行时出现“java.lang.ClassNotFoundException: oracle.jdbc.driver.OracleDriver”错误