要理解循環(huán)依賴關(guān)系,您需要記住Python本質(zhì)上是一種腳本語(yǔ)言。方法外部語(yǔ)句的執(zhí)行在編譯時(shí)發(fā)生。導(dǎo)入語(yǔ)句就像方法調(diào)用一樣執(zhí)行,要理解它們,您應(yīng)該像方法調(diào)用一樣考慮它們。
當(dāng)您進(jìn)行導(dǎo)入時(shí),所發(fā)生的事情取決于您要導(dǎo)入的文件是否已經(jīng)存在于模塊表中。如果使用,Python將使用符號(hào)表中當(dāng)前的任何內(nèi)容。否則,Python將開(kāi)始讀取模塊文件,編譯/執(zhí)行/導(dǎo)入它在那里找到的任何內(nèi)容。編譯時(shí)引用的符號(hào)是否被找到,取決于它們是否已經(jīng)被看到,或者編譯器還沒(méi)有看到它們。
假設(shè)您有兩個(gè)源文件:
文件X.py
def X1:
return "x1"from Y import Y2def X2:
return "x2"
文件Y.py
def Y1:
return "y1"from X import X1def Y2:
return "y2"
現(xiàn)在假設(shè)您編譯了X.py文件。編譯器首先定義X1方法,然后點(diǎn)擊X.py中的import語(yǔ)句。這導(dǎo)致編譯器暫停X.py的編譯并開(kāi)始編譯Y.py。此后不久,編譯器將命中Y.py中的import語(yǔ)句。由于X.py已經(jīng)在模塊表中,Python使用現(xiàn)有的不完整X.py符號(hào)表來(lái)滿足任何請(qǐng)求的引用。在X.py中的import語(yǔ)句之前出現(xiàn)的任何符號(hào)現(xiàn)在都在符號(hào)表中,但是后面的任何符號(hào)都不是。因?yàn)閄1現(xiàn)在出現(xiàn)在IMPORT語(yǔ)句之前,所以它已成功導(dǎo)入。然后Python繼續(xù)編譯Y.py。在這樣做時(shí),它定義了Y2并完成了對(duì)Y.py的編譯。然后繼續(xù)編譯X.py,并在Y.py符號(hào)表中找到Y(jié)2。編譯最終完成w/o錯(cuò)誤。
如果嘗試從命令行編譯Y.py,則會(huì)發(fā)生非常不同的情況。在編譯Y.py時(shí),編譯器在定義Y2之前點(diǎn)擊import語(yǔ)句。然后開(kāi)始編譯X.py。很快,它就會(huì)命中X.py中的import語(yǔ)句,該語(yǔ)句需要Y2。但是Y2沒(méi)有定義,所以編譯失敗。
請(qǐng)注意,如果您修改X.py以導(dǎo)入Y1,編譯將始終成功,無(wú)論您編譯的是哪個(gè)文件。但是,如果您修改文件Y.py以導(dǎo)入符號(hào)X2,則兩個(gè)文件都不會(huì)編譯。
當(dāng)模塊X或X導(dǎo)入的任何模塊可能導(dǎo)入當(dāng)前模塊時(shí),請(qǐng)不要使用:
from X import Y
當(dāng)您認(rèn)為可能存在循環(huán)導(dǎo)入時(shí),您也應(yīng)該避免編譯時(shí)引用其他模塊中的變量??紤]一下看似無(wú)辜的代碼:
import X
z = X.Y
假設(shè)模塊X在這個(gè)模塊導(dǎo)入X之前導(dǎo)入這個(gè)模塊。進(jìn)一步假設(shè)Y是在導(dǎo)入語(yǔ)句之后在X中定義的。在導(dǎo)入該模塊時(shí),將不會(huì)定義Y,您將得到一個(gè)編譯錯(cuò)誤。如果這個(gè)模塊首先導(dǎo)入Y,您就可以逃脫它。但是,當(dāng)您的一個(gè)同事無(wú)意地更改了第三個(gè)模塊中定義的順序時(shí),代碼就會(huì)中斷。
在某些情況下,您可以通過(guò)將導(dǎo)入語(yǔ)句向下移到其他模塊所需的符號(hào)定義下面來(lái)解決循環(huán)依賴關(guān)系。在上面的例子中,導(dǎo)入語(yǔ)句之前的定義永遠(yuǎn)不會(huì)失敗。導(dǎo)入語(yǔ)句后的定義有時(shí)會(huì)失敗,這取決于編譯的順序。您甚至可以將導(dǎo)入語(yǔ)句放在文件的末尾,只要編譯時(shí)不需要導(dǎo)入的符號(hào)。
請(qǐng)注意,在模塊中向下移動(dòng)導(dǎo)入語(yǔ)句會(huì)掩蓋您正在做的事情。在模塊頂部加上一個(gè)注釋來(lái)彌補(bǔ)這一點(diǎn),如下所示:
#import X (actual import moved down to avoid circular dependency)
一般來(lái)說(shuō),這是一個(gè)不好的做法,但有時(shí)是很難避免的。