类导入其他类错误

2022-03-31 00:00:00 python python-import

问题描述

我有三个脚本:

C:codevoiceTermmaster.py:

from voice_terminal_module.voice_terminal import VoiceTerminal

vterm = VoiceTerminal()

C:codevoiceTermvoice_terminal_modulevoice_terminal.py:

from chatbot_module.chatbot_module import Chatbot
class VoiceTerminal:
    print("INITIALIZING VOICE TERMINAL")
    cb = Chatbot()

C:codevoiceTermvoice_terminal_modulechatbot_modulechatbot_module.py:

class Chatbot:
    print("CHATBOT INITIALIZED")

奇怪的是:当我运行chat bot_mode.py时,它会工作,如果我运行VOICE_Terminal.py,它会工作。但是,由于某些原因,master.py会出错,并显示以下消息:

Traceback (most recent call last):
    File "c:codevoiceTermmaster.py", line 1, in <module>
      from voice_terminal_module.voice_terminal import VoiceTerminal
    File "c:codevoiceTermvoice_terminal_modulevoice_terminal.py", line 1, in <module>
      from chatbot_module.chatbot_module import Chatbot
ModuleNotFoundError: No module named 'chatbot_module'

为什么它有时起作用,但有时不起作用?


解决方案

一般导入指南(供您自己编写)

我确定现在应该有一个副本,但我找不到,所以我正在写一篇完整的文章。

要正确解决问题,您必须注意两件事:

  1. 确保sys.path--Python搜索模块的路径--包括项目根目录的路径。

  2. 确保代码中的import语句编写为使用该路径。

搜索路径

对于第一部分,您必须了解sys.path是如何工作的。这是一个文件夹列表,将在启动时自动设置(即使您不是import sys,就像sys.argv一样)。

正常情况下,它将是一个包含以下内容的列表:

  • 要执行的入口点的路径(该路径的详细信息将取决于如何启动Python;当直接从同一目录运行.py文件时,通常为'',如果您这样做,则为另一个目录的路径,例如python somewhere/else/file.py,使用-m开关时为显式路径)

  • 系统库路径

  • 虚拟环境库的路径(如果虚拟环境处于活动状态)

  • 用户显式安装的内容的路径

您可以修改此列表,这将对以后的import语句产生预期影响;但您通常不应。

为确保项目根目录位于路径上,通常只需安装项目--理想情况下应安装到虚拟环境中。详情请参考Python packaging guide。如果做不到这一点,请确保仅从包含顶级代码的文件夹外部启动项目。在OP的例子中,这意味着C:code。这将确保C:code(或等效项)处于sys.path,然后我们可以依赖它进行第二步。

导入

我们基本上可以通过两种方式做到这一点:通过绝对导入指定根目录的路径,或者通过相对导入指定当前源文件的路径。阅读有关相对导入here和here的堆栈溢出的详细信息。

绝对导入

在任何一种情况下,我们都希望将项目的根文件夹(此处为<2-13]>)视为包。对于绝对导入,这意味着我们将始终在导入路径中提及根文件夹名称。

因此:

C:codevoiceTermmaster.py中:from voiceTerm.voice_terminal_module.voice_terminal import VoiceTerminal

C:codevoiceTermvoice_terminal_modulevoice_terminal.py中:from voiceTerm.voice_terminal_module.chatbot_module.chatbot_module import Chatbot

(您不会真的想在文件夹或文件名中包含_module。它并没有真正增加信息,使输入变得相当困难,而且实际上有点误导。)

我们还可以import整个模块:import voiceTerm.masterimport voiceTerm.voice_terminal_module.voice_terminalimport voiceTerm.voice_terminal_module.chatbot_module.chatbot_module。此外,通过将文件命名为__init__.py,我们可以将该文件夹作为包导入:import voiceTermimport voiceTerm.voice_terminal_moduleimport voiceTerm.voice_terminal_module.chatbot_module。我们还可以从以下包之一导入模块:from voiceTerm import masterfrom voiceTerm.voice_terminal_module import voice_terminalfrom voiceTerm.voice_terminal_module.chatbot_module import chatbot_module

相对进口量

对于相对导入,我们必须使用from语法,并且我们只能从我们自己的包层次结构中导入内容。然而,我们的优势在于,我们不必指定完整路径,而且我们可以重命名包,而不必编辑代码。我个人的建议是在可能的情况下使用相对导入;这也与从系统库或其他第三方程序包导入有很强的视觉区别。

对于相对导入,首先使用相对导入路径指定要从中导入的包或子包。从单个.开始,我们从当前目录开始,在同一文件夹中查找另一个模块或包。

因此:

inC:codevoiceTermmaster.pyfrom .voice_terminal_module.voice_terminal import VoiceTerminal导入类;from .voice_terminal_module import voice_terminal导入模块。

inC:codevoiceTermvoice_terminal_modulevoice_terminal.pyfrom .chatbot_module.chatbot_module import Chatbot导入类;from .chatbot_module import chatbot_module导入模块。

其他.在包层次结构中向上导航(但我们不能以这种方式超出根目录)。例如,from ... import master从下层chatbot_module.py导入顶层master.py。(当然,这在本例中实际上不起作用,因为它将是循环导入。)要从同一目录导入另一个源文件(作为模块),只需from . import other_file。你明白了。

相关文章