Python知識(shí)分享網(wǎng) - 專業(yè)的Python學(xué)習(xí)網(wǎng)站 學(xué)Python,上Python222
Python 引用問(wèn)題 - ImportError: attempted relative import with no known parent package
匿名網(wǎng)友發(fā)布于:2023-07-10 13:12:51
(侵權(quán)舉報(bào))

問(wèn)題描述

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

 

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

 

太長(zhǎng)不看版

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

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

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

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

詳細(xì)解釋

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

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)前文件的相對(duì)路徑導(dǎo)入。

第一種方法

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

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

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

可以查看 sys.path,顯然,當(dāng)前運(yùn)行腳本所在的文件夾被放在了搜索路徑的首位,因此該文件夾下的所有內(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']

 

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

 

import sys
sys.path.join("/path/to/root") # 用絕對(duì)路徑,需要從根目錄開(kāi)始
sys.path.join("..") # 用相對(duì)路徑,但是命令行當(dāng)前位置不能出錯(cuò)
 
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 作為腳本運(yùn)行時(shí),即 python code2.py,此時(shí) python 并不會(huì)認(rèn)為它屬于某一個(gè) package, 即使存在 __init__.py??梢?nbsp;print(__package__) 進(jìn)行驗(yàn)證,作為腳本運(yùn)行時(shí)為 None,否則則應(yīng)該為 xxx.yyy 的形式。
(網(wǎng)絡(luò)上有很多地方都說(shuō)添加 __init__.py 就可以解決問(wèn)題,但事實(shí)是并不會(huì) ,在我的測(cè)試中,在本文提到的所有的解決方法中,添加 __init__.py 與否似乎不會(huì)帶來(lái)什么影響。)

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

 

python -m root.folder1.code1

 

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

?這里不帶 .py 后綴。

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

除了命令行調(diào)用時(shí)進(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 并運(yùn)行時(shí),它會(huì)把 root 當(dāng)作一個(gè)包,此時(shí)code2.py中的 from ..folder1.code1 import xxx 可以正常執(zhí)行

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

其他

說(shuō)明:

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