解决导包报错:ModuleNotFoundError,ImportError

1. 问题表现

ImportError: attempted relative import with no known parent package
ModuleNotFoundError: No module named ‘xxx‘

翻译过来大概的意思是:

  1. 找不到父包的情况下进行相对导入
  2. 没有找到xxx模块

2. 尝试去了解

既然找不到父包,那么该如何才能找到它呢?

正如出现上述问题,我个人习惯性直接寻找解决方案,但其实是没深入发掘过,下一次再次遇到了,可能还是知其然不知所以然。

打个比如:某天我正在用浏览器兴致勃勃看着某站视频时,画面突然卡住不动,最先容易想到是不是我的网络断了对吧?

那么这时候,稍微有点网络知识,可能就会去ping一下看看网络状况,进而做下一步诊断。

同样的,在出现上述报错的问题时,使用一个常规方法,即通过sys.path输出的列表,观察Python的环境目录,从而找出错误根源。

3. 先说结论

先说结论:

对于python的应用程序,只需使用绝对路径(不带点哦,如 from .xxx import xxx)一切都会好起来的。

4. 具体问题分析

  • 项目结构和具体程序
"""
├─app
|  ├─mypackge
|	├─module
|      	├─init.py
|       ├─a.py
│       ├─b.py
|       ├─c.py
|  ├─init.py
|  ├─base.py
"""

# base.py
class Base(object):
    print("Base")
    
  
# a.py
from mypackge.base import Base
class A(Base):
    pass


# b.py
from mypackge.base import Base
class B(Base):
    pass


# c.py
from mypackge.base import Base
class C(Base):
    pass

在pycharm中打开上述项目,当我们运行a.py时,一切都很正常,但当我使用command时,python3 a.py,会出现什么问题呢?

出现: ModuleNotFoundError: No module named ‘mypackge‘

好了,回到最先开始提到的,尝试使用sys.path去发现具体问题,把它放在代码最前面:

import sys
print(sys.path)

# 你的代码

# 当使用pycharm解释器输出:
[‘C:\\Users\\Rosesx\\Desktop\\app\\mypackge\\module‘, ‘C:\\Users\\Rosesx\\Desktop\\app‘, ‘D:\\Python\\Python39\\python39.zip‘, ‘D:\\Python\\Python39\\DLLs‘, ‘D:\\Python\\Python39\\lib‘, ‘D:\\Python\\Python39‘, ‘D:\\Python\\Python39\\lib\\site-packages‘, ‘D:\\Python\\Python39\\lib\\site-packages\\win32‘, ‘D:\\Python\\Python39\\lib\\site-packages\\win32\\lib‘, ‘D:\\Python\\Python39\\lib\\site-packages\\Pythonwin‘]

# 当使用command时,python3 a.py,输出:
[‘C:\\Users\\Rosesx\\Desktop\\app\\mypackge\\module‘, ‘D:\\Python\\Python39\\python39.zip‘, ‘D:\\Python\\Python39\\DLLs‘, ‘D:\\Python\\Python39\\lib‘, ‘D:\\Python\\Python39‘, ‘D:\\Python\\Python39\\lib\\site-packages‘, ‘D:\\Python\\Python39\\lib\\site-packages\\win32‘, ‘D:\\Python\\Python39\\lib\\site-packages\\win32\\lib‘, ‘D:\\Python\\Python39\\lib\\site-packages\\Pythonwin‘]
Traceback (most recent call last):
  File "C:\Users\Rosesx\Desktop\app\mypackge\module\a.py", line 15, in <module>
    from mypackge.base import Base
ModuleNotFoundError: No module named ‘mypackge‘

注意观察是不是发现什么?pycharm和command解释器分别输出的内容中,pycharm打印的环境目录列表多了一个项目app绝对路径

是的,无论你把sys.path放到项目中那个.py中去执行,都会看到有一个项目的绝对路径。

直到现在,我猜你似乎已经发现 ModuleNotFoundError问题具体是什么了。

  • 做个测试
# a.py
import sys
from pathlib import Path

for path in sys.path:
    A = Path(path, r‘mypackge\base.py‘) # 遍历python环境目录然后拼接mypackge\base.py
    B = r‘C:\Users\Rosesx\Desktop\app\mypackge\base.py‘ # 预期输入
    if str(A) == B: # 测试一下实际输出结果A和预期输入B
        print(True) 
    else:
        print(False)

from mypackge.base import Base

class A(Base):
    pass

# 当使用pycharm解释器输出:
False
True
False
False
False
False
False
False
False
False
Base

# 当使用command时,python3 a.py,输出:
False
False
False
False
False
False
False
False
False
Traceback (most recent call last):
  File "C:\Users\Rosesx\Desktop\app\mypackge\module\a.py", line 15, in <module>
    from mypackge.base import Base
ModuleNotFoundError: No module named ‘mypackge‘

经过这么一个小测试,发现使用pycharm解释器输出的实际结果有一条返回True,那么是不是可以说明from mypackge.base import Base成功搜索到它的上级,也就是匹配上了绝对路径。而command输出并没有找到自己的上级而报错。

5. 解决思路

顺着上面得到的验证,当使用command执行python3 a.py时,我们发现它没有导入父模块(也就是说没有找到项目绝对路径)。

这就好办了,在不改动from mypackge.base import Base前提下,加入mypackge的上级目录

# a.py
import sys
from pathlib import Path

sys.path.append(r‘C:\Users\Rosesx\Desktop\app‘) # 加入mypackge的父模块

from mypackge.base import Base

class A(Base):
    pass

也可以这样写:

a.py
import sys
from pathlib import Path

path = Path(__file__).parent.parent.parent  # 链式调用,返回当前a.py的上上上目录
print(path)  # 输出: mypackge上级路径

sys.path.append(str(path))

from mypackge.base import Base


class A(Base):
    pass

6. 总结

是否在看某个库的源代码时,发现它们会使用from ..xxx import xxx 或 from .xxx import xxx导入模块,当我们也这样做的时,就出现导入报错。

.module(带点)是相对导入,相对仅在已先导入或已加载父模块时才有效果,这就意味着你需要在当前运行环境中的某处已导入。当使用command执行python3 module.py,它不会事先导入父模块,你需要自行导入,或者from youproject.moduleA import moduleA这样的方式调用。

不管是怎么写,它们都应遵循着一个原则:对于python的应用程序,只需使用绝对路径,一切都会好起来

解决导包报错:ModuleNotFoundError,ImportError

上一篇:centos7安装redis6.0


下一篇:==和equals的区别