Python知識分享網(wǎng) - 專業(yè)的Python學(xué)習(xí)網(wǎng)站 學(xué)Python,上Python222
Python 引用問題 - ImportError: attempted relative import with no known parent package
發(fā)布于:2023-07-10 13:12:51

問題描述

近日在嘗試引用其他文件的代碼時,遇到了錯誤: ImportError: attempted relative import with no known parent package.
問題大致是這樣的:我想在 code2.py 中引用 code1.py 的函數(shù),如 from ..folder1.code1 import xxx,運行 code2.py 時出現(xiàn)錯誤。

 

root
├── folder1
│   └── code1.py
├── folder2
│   └── code2.py
└── main.py

 

太長不看版

如果你要在 code2.py 中引用 code1.py 的函數(shù),那么可以:

改變文件結(jié)構(gòu),考慮在 main.py 中調(diào)用,運行 main.py

code2.py 中增加 root 的位置到搜索路徑 sys.path.append, 代碼使用 from folder1.code1 import xxx

用 -m 選項運行: python -m root.folder2.code2,代碼可以使用 from folder1.code1 import xxx 或 from ..folder1.code1 import xxx [我認(rèn)為這是最優(yōu)解!]

詳細(xì)解釋

如果對導(dǎo)入的概念不是很理解的話,可能會遇到:

ModuleNotFoundError: No module named 'xxx'

ImportError: attempted relative import with no known parent package

首先明確兩種導(dǎo)入方法:

  1. from xxx import yyy 則是從已知的模塊導(dǎo)入
  2. “relative import” 即 from .xxx import yyy,根據(jù)從當(dāng)前文件的相對路徑導(dǎo)入。

第一種方法

具體可參考官方文檔 the-module-search-path

僅適用于模塊(文件夾)或腳本(文件)存在于搜索路徑中,導(dǎo)入時,Python 解釋器會首先搜索內(nèi)置模塊,如果沒有,則去以下三個位置搜索:

  1. 當(dāng)前文件所在目錄
  2. 環(huán)境變量 PYTHONPATH 指定的目錄
  3. Python 默認(rèn)的安裝目錄

可以查看 sys.path,顯然,當(dāng)前運行腳本所在的文件夾被放在了搜索路徑的首位,因此該文件夾下的所有內(nèi)容均可被引入。

 

import sys
print(sys.path)
# ['/.../path-to-this-folder', '/usr/lib/python310.zip', '/usr/lib/python3.10', '/usr/lib/python3.10/lib-dynload', '/home/thor/.local/lib/python3.10/site-packages', '/usr/local/lib/python3.10/dist-packages', '/usr/lib/python3/dist-packages']

 

要解決開頭提出的問題,即引入其他文件夾下的內(nèi)容,可以把 root 的位置添加到搜索路徑中:(好吧,這樣很不優(yōu)雅……)

 

import sys
sys.path.join("/path/to/root") # 用絕對路徑,需要從根目錄開始
sys.path.join("..") # 用相對路徑,但是命令行當(dāng)前位置不能出錯
 
from folder1.code1 import xxx

 

可以參考這段代碼:

 

if __package__:
    from .. import config
else:
    sys.path.append(os.dirname(__file__) + '/..')
    import config

 

第二種方法

具體可參考官方文檔 packages

需要明確的是,這種方法只適用于 package 內(nèi)部!
當(dāng)你把 code2.py 作為腳本運行時,即 python code2.py,此時 python 并不會認(rèn)為它屬于某一個 package, 即使存在 __init__.py。可以 print(__package__) 進(jìn)行驗證,作為腳本運行時為 None,否則則應(yīng)該為 xxx.yyy 的形式。
(網(wǎng)絡(luò)上有很多地方都說添加 __init__.py 就可以解決問題,但事實是并不會 ,在我的測試中,在本文提到的所有的解決方法中,添加 __init__.py 與否似乎不會帶來什么影響。)

因此,開頭描述的問題中,要使用相對導(dǎo)入的形式在 code2.py 中引用 code1.py 的代碼,必須使用:

 

python -m root.folder1.code1

 

這里把 root 及其內(nèi)部當(dāng)作一個完整的 package,而 package 內(nèi)的腳本可以使用相對導(dǎo)入互相引用。

?這里不帶 .py 后綴。

?不可以為 python -m folder1.code1,此時把 folder1 及其內(nèi)部當(dāng)作一個完整的 package, 無法引用到以外的內(nèi)容,會遇到 ImportError: attempted relative import beyond top-level package

除了命令行調(diào)用時進(jìn)行調(diào)整,在腳本中 import 也是一樣的道理:

 

newroot
├── root
│?? ├── folder1
│?? │?? └── code1.py
│?? ├── folder2
│?? │?? └── code2.py
│?? └── main.py
└── upper_main.py

 

在 upper_main.py 中添加 from root.folder2 import code2 并運行時,它會把 root 當(dāng)作一個包,此時code2.py中的 from ..folder1.code1 import xxx 可以正常執(zhí)行

在 main.py 中添加 import folder2.code2 并運行時,它會把 folder2 當(dāng)作一個包,此時 code2.py 中的 from .xx import 可以正常執(zhí)行,而 from ..folder1.code1 import xxx 會遇到 ImportError: attempted relative import beyond top-level package.

其他

說明:

  1. 這里僅說明我嘗試成功得出的經(jīng)驗,不排除有其他正確做法。
  2. 我還看到過類似 code2.py 中有 from folder1 import code1 這種做法,沒有測試過其適用條件,不過模塊內(nèi)部感覺使用相對引用比較好。
轉(zhuǎn)載自:https://www.cnblogs.com/zkmjolnir/p/17535294.html