Python导入 - ModuleNotFoundError: 找不到名为X的模块。

5

我已经阅读了这里关于导入的所有帖子,但我仍然无法弄清楚导入的情况。我花了几个小时尝试让一个非常简单的例子运行起来,现在我都快要抓狂了。

我正在使用Python 3.7和PyCharm,但我是从命令行运行我的代码,在单元测试中我使用pytest。

我的项目结构如下:

my_message_validator/
    __init__.py

    module_1/
        __init.py__
        foo.py

    module_2/
        __init.py__
        bar.py
        baz.py

    module_3
        context.py
        test_all.py

module_1.init.py

from module_1 import foo

module_2.init.py

# For some reason pycharm doesnt complain when I use '.' but if I use module_2 it does
from . import bar, baz

如果我试图从命令行运行我的代码或测试,无论我如何移动东西,似乎都会出现“ModuleNotFoundError: No module named”错误。即使我设法让测试工作,我仍然无法从命令行单独运行我的代码。
如何将module_1导入module_2,并且这些实际上是软件包吗?我来自java,发现导入更容易理解,我发现python的导入非常令人困惑...
另外,我怎样才能将我需要的任何内容导入到我的测试模块/软件包/文件夹context.py中?
目前测试上下文如下:
import os
import sys

# Is this needed as it doesnt seem to do anything?
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

from module_1.foo import Foo

from module_2 import bar, baz

test_all.py 文件中,我尝试像这样从上下文文件进行导入:
from .context import bar,baz
from .context import Foo

# Calling in test like
Foo.load_file(file)

bar.method_one()
baz.method_two()

我需要所有的__init.py__文件吗?我应该在其中放置什么来使我的方法和类公开/暴露?我希望这整个包可重用,所以希望能够像Java中的jar文件一样对待它。
非常感谢任何帮助,因为似乎每次我改变一些东西,就会在不同的地方出现错误,现在Python看起来比Java更加复杂。

你尝试过这样导入吗? from my_message_validator.module_1.foo import Foo - Pratibha Gupta
3个回答

8
首先,不要使用相对导入(带有 . ),因为已知会导致多个问题。始终将导入写成相对于项目根目录的形式。例如,您在from module_1.foo import Foo中做得很好。您还应该在test_all.pycontext.py中这样做。此外,在使用相对导入后,您的情况下可以将__init__.py文件留空。
最有可能的是,Python解释器无法找到您的模块,因为PYTHONPATH环境变量不包含您的项目根目录。如果在脚本之前运行export PYTHONPATH="YOUR_PROJECT_ROOT_ABSOLUTE_PATH:$PYTHONPATH",则应按预期运行。为确保始终设置此变量,可以将导出语句添加到您的shell配置文件中(例如.bashrc.bash_profile)。
在与作者聊天后,发现第四个问题是一个名称冲突,就像在其他问题中一样。在他的项目目录中,module_1实际上被称为foo,就像它的子代foo.py一样,这让解释器感到困惑。

那你建议我删除所有的 __init__.py 文件吗?如果 barbaz 只包含函数而不是类,导入它们的正确方式是什么? - berimbolo
好的,感谢您的帮助。我已经将所有内容都精简了一下,以便没有__init.py__文件和测试上下文。不幸的是,现在我甚至无法在bar中导入baz,我的测试失败并显示ModuleNotFoundError: No module named 'module_1'。我按照您的建议导出了PYTHONPATH,但是没有一个建议起作用。 - berimbolo
让我们在聊天中继续这个讨论 - Pierre
PEP 420-隐式命名空间包中可以得知:“常规包将继续拥有__init__.py并驻留于单个目录。”另外,也可以参考https://dev59.com/71oU5IYBdhLWcg3wxIyA#48804718。 - djvg
1
@djvg 感谢您的评论。我同意,并且想补充一点,一些代码检查工具或静态分析工具不会探索那些不是常规包的目录。我已经编辑了答案。 - Pierre
显示剩余4条评论

0

我遇到了相同的情况。
我用以下方式启动了应用程序:

flask run

每次我在网站上遇到 ModuleNotFoundError 错误时,
我会像这样启动应用程序:

python3 -m flask run

应用程序已经成功启动,没有出现任何错误 :-)。


0

你尝试过像这样导入吗:

from my_message_validator.module_1.foo import Foo
from my_message_validator.module_2 import bar, baz

如果我使用绝对路径而不是相对路径,那么我是否需要担心__init__.py文件呢? - berimbolo
当我将绝对路径添加到我的单元测试中时,我会得到这个错误而不是 ModuleNotFoundError: No module named 'my_message_validator',尽管PyCharm似乎对此格式感到满意。 - berimbolo
由于Python识别您的项目路径是通过根目录,因此需要绝对路径来导入。您需要在根目录之后提及其他模块。 - Pratibha Gupta
你需要从根目录开始提及其他模块。能否给出一个例子更新一下你的回答?我不知道你的意思。 - berimbolo
抱歉回答有些混淆。如果my_message_validator是您项目的根路径,您需要从my_message_validator开始导入模块。除非其他模块也在同一目录中,否则python无法识别您的内部模块。 - Pratibha Gupta
显示剩余2条评论

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