作者?|?小帥b
來(lái)源 |?學(xué)習(xí)python的正確姿勢(shì)
當(dāng)你去看一些 Python 相關(guān)的項(xiàng)目時(shí),常常會(huì)看到?__init__.py,當(dāng)你使用某些編輯器創(chuàng)建 Python?Package 的時(shí)候,它也會(huì)自動(dòng)給你生成一個(gè)?__init__.py 文件。我們知道, Python 中的包是可以包含多個(gè) py 模塊的,我們可以在不同的地方通過(guò)包名區(qū)分使用這些模塊。話不多說(shuō),咱們先來(lái)創(chuàng)建一下這樣的目錄:我們?cè)谶@里面創(chuàng)建了三個(gè) Python 子包,里面都有 __init__ 和 module 的 py 文件。
我們分別在不同包下的?__init__ 中寫(xiě)一個(gè) print 語(yǔ)句:
接著我們進(jìn)入 Python ,分別來(lái)導(dǎo)入這些模塊:
可以看到,當(dāng)我們導(dǎo)入父模塊中的子模塊的時(shí)候,它會(huì)優(yōu)先執(zhí)行父模塊中的 init ,接著會(huì)執(zhí)行指定模塊中的 init。當(dāng)然,只是導(dǎo)入父模塊的時(shí)候只會(huì)執(zhí)行父模塊中的 init:也就是說(shuō),當(dāng)我們?nèi)?import 一個(gè) Package 的時(shí)候,它會(huì)隱性的去執(zhí)行 __init__.py ,?而在 __init__.py 中定義的對(duì)象,會(huì)被綁定到當(dāng)前的命名空間里面來(lái)。比如有時(shí)候我們會(huì)這樣去導(dǎo)入一個(gè)包下的所有模塊,會(huì)這樣操作:
但這個(gè)時(shí)候你會(huì)發(fā)現(xiàn)并沒(méi)有將相關(guān)的子模塊導(dǎo)入進(jìn)來(lái):這時(shí)候你可能想到了,可以在父模塊中的?__init__.py 做文章,先把它們導(dǎo)入進(jìn)來(lái)不就行了:這里的?__all__ 相當(dāng)于導(dǎo)入?[] 里面定義的模塊。
可以看到,所有子模塊就都一并導(dǎo)入進(jìn)來(lái)了。當(dāng)然,你也可以在 __init__.py 做一些初始化的操作,比如數(shù)據(jù)庫(kù) session 的創(chuàng)建:其實(shí)在 Python3.2 版本之前,定義的 Package 下面一定要有?__init__.py 文件,這樣 Python 才知道它是一個(gè) Package,才可以尋找到相關(guān)模塊的路徑從而被 import。而在 Python3.2 之后的版本就不需要再額外的去專(zhuān)門(mén)創(chuàng)建一個(gè) __init__.py 來(lái)告訴 Python 它是一個(gè) Package 了,因?yàn)楝F(xiàn)在創(chuàng)建的包叫 Namespace package, Python 可以自動(dòng)搜尋 Package 路徑,哪怕你的父包路徑發(fā)生了改變,你在下次導(dǎo)入的時(shí)候, Python 還是會(huì)自動(dòng)重新搜索包路徑。我們把剛剛定義的?__init__?都給刪掉試試:接著我們?cè)?Python3.8 版本導(dǎo)入看看:
可以看到,盡管我們?cè)?package 中沒(méi)有定義 __init__.py,依然可以導(dǎo)入使用。以同樣的形式,我們?cè)?Python2 中導(dǎo)入看看:
綜上,__init__.py?會(huì)在 import 的時(shí)候被執(zhí)行,而空的 __init__.py 在 Python 新版本中已經(jīng)不需要你額外去定義了,因?yàn)榫退隳悴欢x init, Python 也知道你導(dǎo)入的包路徑,但是如果你想要做一些初始化操作,或者像我們剛剛說(shuō)的預(yù)先導(dǎo)入相關(guān)的模塊,那么定義 __init__.py 還是很有必要的喲。