1. 程式人生 > 其它 >解決導包報錯:ModuleNotFoundError,ImportError

解決導包報錯: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的應用程式,只需使用絕對路徑,一切都會好起來