你在Python中遇到過(guò)循環(huán)導(dǎo)入嗎?嗯,這是一種非常常見(jiàn)的代碼味道,表明設(shè)計(jì)或結(jié)構(gòu)有問(wèn)題。
循環(huán)導(dǎo)入示例
循環(huán)導(dǎo)入是如何發(fā)生的?當(dāng)兩個(gè)或多個(gè)相互依賴的模塊在完全初始化之前嘗試導(dǎo)入時(shí),通常會(huì)發(fā)生此導(dǎo)入錯(cuò)誤。
假設(shè)我們有兩個(gè)模塊:module_1.py 和 module_2.py。
# module_1.py from module_2 import ModY class ModX: mody_obj = ModY()
# module_2.py from module_1 import ModX class ModY: modx_obj = ModX()
在上面的代碼片段中,module_1和module_2都是相互依賴的。
module_1 中 mody_obj 的初始化依賴于 module_2,module_2 中 modx_obj 的初始化依賴于 module_1。
這就是我們所說(shuō)的循環(huán)依賴。兩個(gè)模塊在嘗試互相加載時(shí)都會(huì)陷入導(dǎo)入循環(huán)。
如果我們運(yùn)行 module_1.py,我們將得到以下回溯。
Traceback (most recent call last): File "module_1.py", line 1, in <module> from module_2 import ModY File "module_2.py", line 1, in <module> from module_1 import ModX File "module_1.py", line 1, in <module> from module_2 import ModY ImportError: cannot import name 'ModY' from partially initialized module 'module_2' (most likely due to a circular import)
這個(gè)錯(cuò)誤說(shuō)明了循環(huán)導(dǎo)入的情況。當(dāng)程序嘗試從 module_2 導(dǎo)入 ModY 時(shí),當(dāng)時(shí) module_2 尚未完全初始化(由于另一個(gè)導(dǎo)入語(yǔ)句嘗試從 module_1 導(dǎo)入 ModX)。
如何修復(fù) Python 中的循環(huán)導(dǎo)入? ??有多種方法可以消除 Python 中的循環(huán)導(dǎo)入。
修復(fù) Python 中的循環(huán)導(dǎo)入
將代碼移至公共文件中
我們可以將代碼移動(dòng)到一個(gè)公共文件中以避免導(dǎo)入錯(cuò)誤,然后嘗試從該文件導(dǎo)入模塊。
# main.py ----> common file class ModX: pass class ModY: pass
在上面的代碼片段中,我們將類 ModX 和 ModY 移動(dòng)到一個(gè)公共文件 (main.py) 中。
# module_1.py from main import ModY class Mod_X: mody_obj = ModY()
# module_2.py from main import ModX class Mod_Y: modx_obj = ModX()
現(xiàn)在,module_1 和 module_2 從 main 導(dǎo)入類,修復(fù)了循環(huán)導(dǎo)入的情況。
這種方法有一個(gè)問(wèn)題,有時(shí)代碼庫(kù)太大,將代碼移動(dòng)到另一個(gè)文件中會(huì)存在風(fēng)險(xiǎn)。
將導(dǎo)入移至模塊末尾
我們可以將導(dǎo)入語(yǔ)句移到模塊末尾。這將為導(dǎo)入另一個(gè)模塊之前留出時(shí)間來(lái)完全初始化模塊。
# module_1.py class ModX: pass from module_2 import ModY class Mod_X: mody_obj = ModY()
# module_2.py class ModY: pass from module_1 import ModX
在類/函數(shù)范圍內(nèi)導(dǎo)入模塊
在類或函數(shù)作用域內(nèi)導(dǎo)入模塊可以避免循環(huán)導(dǎo)入。這允許僅在調(diào)用類或函數(shù)時(shí)導(dǎo)入模塊。當(dāng)我們想要最小化內(nèi)存使用時(shí),它是相關(guān)的。
# module_1.py class ModX: pass class Mod_X: from module_2 import ModY mody_obj = ModY()
# module_2.py class ModY: pass class Mod_Y: from module_1 import ModX modx_obj = ModX()
我們分別將 import 語(yǔ)句移至 module_1 和 module_2 中的類 Mod_X 和 Mod_Y 范圍內(nèi)。
如果我們運(yùn)行 module_1 或 module_2,我們不會(huì)收到循環(huán)導(dǎo)入錯(cuò)誤。但是,這種方法使得類只能在類的范圍內(nèi)訪問(wèn),因此我們無(wú)法全局利用導(dǎo)入。
使用模塊名稱/別名
使用模塊名稱或僅像這樣的別名可以解決問(wèn)題。這允許兩個(gè)模塊通過(guò)將循環(huán)依賴推遲到運(yùn)行時(shí)來(lái)完全加載。
# module_1.py from module_2 import ModY class ModX: mody_obj = ModY()
# module_2.py from module_1 import ModX class ModY: modx_obj = ModX()
使用 importlib 庫(kù)
我們還可以使用 importlib 庫(kù)動(dòng)態(tài)導(dǎo)入模塊。
Traceback (most recent call last): File "module_1.py", line 1, in <module> from module_2 import ModY File "module_2.py", line 1, in <module> from module_1 import ModX File "module_1.py", line 1, in <module> from module_2 import ModY ImportError: cannot import name 'ModY' from partially initialized module 'module_2' (most likely due to a circular import)
# main.py ----> common file class ModX: pass class ModY: pass
Python 包中的循環(huán)導(dǎo)入
通常,循環(huán)導(dǎo)入 來(lái)自同一包內(nèi)的模塊。在復(fù)雜的項(xiàng)目中,目錄結(jié)構(gòu)也很復(fù)雜,包內(nèi)包。
這些包和子包包含 __init__.py 文件,以提供更輕松的模塊訪問(wèn)。這就是有時(shí)無(wú)意中在模塊之間產(chǎn)生循環(huán)依賴的地方。
我們有以下目錄結(jié)構(gòu)。
# module_1.py from main import ModY class Mod_X: mody_obj = ModY()
我們有一個(gè) mainpkg 包和一個(gè) main.py 文件。我們?cè)?mainpkg 中有兩個(gè)子包 modpkg_x 和 modpkg_y。
這是 modpkg_x 和 modpkg_y 中每個(gè) Python 文件的樣子。
mainpkg/modpkg_x/__init__.py
# module_2.py from main import ModX class Mod_Y: modx_obj = ModX()
此文件從 module_1 和 module_1_1 導(dǎo)入兩個(gè)類(ModX 和 ModA)。
mainpkg/modpkg_x/module_1.py
# module_1.py class ModX: pass from module_2 import ModY class Mod_X: mody_obj = ModY()
module_1 從 module_2 導(dǎo)入 ModY 類。
mainpkg/modpkg_x/module_1_1.py
# module_2.py class ModY: pass from module_1 import ModX
module_1_1 沒(méi)有導(dǎo)入任何內(nèi)容。它不依賴于任何模塊。
mainpkg/modpkg_y/__init__.py
# module_1.py class ModX: pass class Mod_X: from module_2 import ModY mody_obj = ModY()
此文件從 module_2 導(dǎo)入 ModY 類。
mainpkg/modpkg_y/module_2.py
# module_2.py class ModY: pass class Mod_Y: from module_1 import ModX modx_obj = ModX()
module_2 從 module_1_1 導(dǎo)入 ModA 類。
我們?cè)?main.py 文件中有以下代碼。
root_dir/main.py
# module_1.py import module_2 as m2 class ModX: def __init__(self): self.mody_obj = m2.ModY()
主文件從 module_2 導(dǎo)入 ModY 類。該文件依賴于 module_2。
如果我們?cè)谶@里可視化導(dǎo)入周期,它看起來(lái)像下面忽略 modpkg_x 和 modpkg_y 中的 __init__.py 文件。
我們可以看到主文件依賴于module_2,module_1也依賴于module_2,module_2又依賴于module_1_1。沒(méi)有導(dǎo)入周期。
但是你知道,模塊依賴于它們的 __init__.py 文件,因此 __init__.py 文件首先初始化,然后重新導(dǎo)入模塊。
這就是現(xiàn)在的導(dǎo)入周期。
這使得 module_1_1 依賴于 module_1,這是一個(gè)假依賴項(xiàng)。
如果是這種情況,清空子包 __init__.py 文件并使用單獨(dú)的 __init__.py 文件可以幫助在包級(jí)別集中導(dǎo)入。
# module_1.py from module_2 import ModY class ModX: mody_obj = ModY()
在此結(jié)構(gòu)中,我們?cè)?mainpkg 中添加了另一個(gè)子包 subpkg。
mainpkg/subpkg/__init__.py
# module_2.py from module_1 import ModX class ModY: modx_obj = ModX()
這將允許內(nèi)部模塊從單一來(lái)源導(dǎo)入,從而減少交叉導(dǎo)入的需要。
現(xiàn)在我們可以更新 main.py 文件中的導(dǎo)入語(yǔ)句。
root_dir/main.py
Traceback (most recent call last): File "module_1.py", line 1, in <module> from module_2 import ModY File "module_2.py", line 1, in <module> from module_1 import ModX File "module_1.py", line 1, in <module> from module_2 import ModY ImportError: cannot import name 'ModY' from partially initialized module 'module_2' (most likely due to a circular import)
這解決了同一包內(nèi)模塊之間的循環(huán)依賴問(wèn)題。
結(jié)論
Python 中的循環(huán)依賴或?qū)胧且环N代碼異味,這表明需要對(duì)代碼進(jìn)行認(rèn)真的重構(gòu)和重構(gòu)。
您可以嘗試上述任何一種方法來(lái)避免 Python 中的循環(huán)依賴。
?如果您喜歡這篇文章,您可能還會(huì)感興趣
?Flask 中的模板繼承示例。
?exec() 和 eval() 之間的區(qū)別并舉例。
?了解Python中g(shù)lobal關(guān)鍵字的使用。
?Python 類型提示:函數(shù)、返回值、變量。
?為什么在函數(shù)定義中使用斜線和星號(hào)。
?學(xué)習(xí)率如何影響 ML 和 DL 模型?
現(xiàn)在就這些。
繼續(xù)編碼??。
以上是在 Python 中修復(fù)循環(huán)導(dǎo)入的不同方法的詳細(xì)內(nèi)容。更多信息請(qǐng)關(guān)注PHP中文網(wǎng)其他相關(guān)文章!

熱AI工具

Undress AI Tool
免費(fèi)脫衣服圖片

Undresser.AI Undress
人工智能驅(qū)動(dòng)的應(yīng)用程序,用于創(chuàng)建逼真的裸體照片

AI Clothes Remover
用于從照片中去除衣服的在線人工智能工具。

Clothoff.io
AI脫衣機(jī)

Video Face Swap
使用我們完全免費(fèi)的人工智能換臉工具輕松在任何視頻中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費(fèi)的代碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
功能強(qiáng)大的PHP集成開(kāi)發(fā)環(huán)境

Dreamweaver CS6
視覺(jué)化網(wǎng)頁(yè)開(kāi)發(fā)工具

SublimeText3 Mac版
神級(jí)代碼編輯軟件(SublimeText3)

處理API認(rèn)證的關(guān)鍵在于理解并正確使用認(rèn)證方式。1.APIKey是最簡(jiǎn)單的認(rèn)證方式,通常放在請(qǐng)求頭或URL參數(shù)中;2.BasicAuth使用用戶名和密碼進(jìn)行Base64編碼傳輸,適合內(nèi)部系統(tǒng);3.OAuth2需先通過(guò)client_id和client_secret獲取Token,再在請(qǐng)求頭中帶上BearerToken;4.為應(yīng)對(duì)Token過(guò)期,可封裝Token管理類自動(dòng)刷新Token;總之,根據(jù)文檔選擇合適方式,并安全存儲(chǔ)密鑰信息是關(guān)鍵。

在Python中同時(shí)遍歷兩個(gè)列表的常用方法是使用zip()函數(shù),它會(huì)按順序配對(duì)多個(gè)列表并以最短為準(zhǔn);若列表長(zhǎng)度不一致,可使用itertools.zip_longest()以最長(zhǎng)為準(zhǔn)并填充缺失值;結(jié)合enumerate()可同時(shí)獲取索引。1.zip()簡(jiǎn)潔實(shí)用,適合成對(duì)數(shù)據(jù)迭代;2.zip_longest()處理不一致長(zhǎng)度時(shí)可填充默認(rèn)值;3.enumerate(zip())可在遍歷時(shí)獲取索引,滿足多種復(fù)雜場(chǎng)景需求。

Inpython,IteratorSareObjectSthallowloopingThroughCollectionsByImplementing_iter __()和__next __()。1)iteratorsWiaTheIteratorProtocol,使用__ITER __()toreTurnterateratoratoranteratoratoranteratoratorAnterAnteratoratorant antheittheext__()

要使用Python創(chuàng)建現(xiàn)代高效的API,推薦使用FastAPI;其基于標(biāo)準(zhǔn)Python類型提示,可自動(dòng)生成文檔,性能優(yōu)越。安裝FastAPI和ASGI服務(wù)器uvicorn后,即可編寫接口代碼。通過(guò)定義路由、編寫處理函數(shù)并返回?cái)?shù)據(jù),可以快速構(gòu)建API。FastAPI支持多種HTTP方法,并提供自動(dòng)生成的SwaggerUI和ReDoc文檔系統(tǒng)。URL參數(shù)可通過(guò)路徑定義捕獲,查詢參數(shù)則通過(guò)函數(shù)參數(shù)設(shè)置默認(rèn)值實(shí)現(xiàn)。合理使用Pydantic模型有助于提升開(kāi)發(fā)效率和準(zhǔn)確性。

要測(cè)試API需使用Python的Requests庫(kù),步驟為安裝庫(kù)、發(fā)送請(qǐng)求、驗(yàn)證響應(yīng)、設(shè)置超時(shí)與重試。首先通過(guò)pipinstallrequests安裝庫(kù);接著用requests.get()或requests.post()等方法發(fā)送GET或POST請(qǐng)求;然后檢查response.status_code和response.json()確保返回結(jié)果符合預(yù)期;最后可添加timeout參數(shù)設(shè)置超時(shí)時(shí)間,并結(jié)合retrying庫(kù)實(shí)現(xiàn)自動(dòng)重試以增強(qiáng)穩(wěn)定性。

在Python中,函數(shù)內(nèi)部定義的變量是局部變量,僅在函數(shù)內(nèi)有效;外部定義的是全局變量,可在任何地方讀取。1.局部變量隨函數(shù)執(zhí)行結(jié)束被銷毀;2.函數(shù)可訪問(wèn)全局變量但不能直接修改,需用global關(guān)鍵字;3.嵌套函數(shù)中若要修改外層函數(shù)變量,需使用nonlocal關(guān)鍵字;4.同名變量在不同作用域互不影響;5.修改全局變量時(shí)必須聲明global,否則會(huì)引發(fā)UnboundLocalError錯(cuò)誤。理解這些規(guī)則有助于避免bug并寫出更可靠的函數(shù)。

是的,你可以使用Python和Pandas解析HTML表格。首先,使用pandas.read_html()函數(shù)提取表格,該函數(shù)可將網(wǎng)頁(yè)或字符串中的HTML元素解析為DataFrame列表;接著,若表格無(wú)明確列標(biāo)題,可通過(guò)指定header參數(shù)或手動(dòng)設(shè)置.columns屬性修復(fù);對(duì)于復(fù)雜頁(yè)面,可結(jié)合requests庫(kù)獲取HTML內(nèi)容或使用BeautifulSoup定位特定表格;注意JavaScript渲染、編碼問(wèn)題及多表識(shí)別等常見(jiàn)陷阱。

在Python中訪問(wèn)嵌套JSON對(duì)象的方法是先明確結(jié)構(gòu),再逐層索引。首先確認(rèn)JSON的層級(jí)關(guān)系,例如字典嵌套字典或列表;接著使用字典鍵和列表索引逐層訪問(wèn),如data"details"["zip"]獲取zip編碼,data"details"[0]獲取第一個(gè)愛(ài)好;為避免KeyError和IndexError,可用.get()方法設(shè)置默認(rèn)值,或封裝函數(shù)safe_get實(shí)現(xiàn)安全訪問(wèn);對(duì)于復(fù)雜結(jié)構(gòu),可遞歸查找或使用第三方庫(kù)如jmespath處理。
