|
模塊 我們從一個(gè)常見(jiàn)的python代碼開(kāi)始 很多人,我也不例外,把它當(dāng)成固定格式,而不去深入理解它。 我們已經(jīng)知道一點(diǎn),當(dāng)從CLI調(diào)用你的代碼而不是導(dǎo)入它時(shí),這個(gè)代碼片段會(huì)有所不同。 現(xiàn)在讓我們?cè)囍ダ斫馕覀優(yōu)槭裁葱枰盟?/p> 為了說(shuō)明,假設(shè)我們正在編寫(xiě)一款披薩店軟件。 源碼在Github上。 這是pizza.py文件。 我已經(jīng)添加了打印__name__變量的代碼,以便了解__name__是如何變化的。 的確,全局變量__name__在從CLI調(diào)用的時(shí)候設(shè)置成了“__main__”。 可是如果從另外一個(gè)文件中引用它會(huì)怎么樣呢?以下是menu.py的源碼: 運(yùn)行menu.py 接著看看下面兩點(diǎn):
所以,事實(shí)是,__name__是保存當(dāng)前Python模塊名稱的全局變量。
那么到底什么是模塊呢? 這非常簡(jiǎn)單 - 模塊是一個(gè)包含Python代碼的文件,可以使用解釋器(python程序)執(zhí)行或從其他模塊導(dǎo)入。
就像執(zhí)行時(shí)一樣,當(dāng)模塊被導(dǎo)入時(shí),它的頂級(jí)語(yǔ)句也會(huì)被執(zhí)行,但是要知道,即使從不同的文件中導(dǎo)入它幾次,它也只會(huì)被執(zhí)行一次。
因?yàn)槟K只是純文件,所以有一個(gè)簡(jiǎn)單的方法來(lái)導(dǎo)入它們。 只取文件名,去掉.py擴(kuò)展名并將其放入import語(yǔ)句中。
有趣的是,__name__被設(shè)置為文件名,無(wú)論你如何導(dǎo)入它 - 例如import pizza as broccoli,__name__仍然是pizza。 所以
但是如果導(dǎo)入的模塊不在同一個(gè)目錄下,我們?cè)趺磳?dǎo)入呢? 答案是放在模塊搜索路徑中,我們最終會(huì)在討論包時(shí)研究它。 包
命名空間部分很重要,因?yàn)樗旧聿⒉惶峁┤魏喂δ?- 它只是給你一個(gè)組合你所有模塊的方式。 兩種情況下,你需要把模塊放入一個(gè)包中。 首先是隔離一個(gè)模塊的定義。 在我們的pizza模塊中,我們有一個(gè)可能與其他Pizza包相沖突的Pizza類(我們?cè)趐ypi上有一些pizza包) 第二種情況是,如果你想分發(fā)你的代碼,因?yàn)?/p>
你在PyPI上看到的所有東西都是通過(guò)pip安裝的,所以為了分享你的東西,你必須把它做成一個(gè)包。 好吧,假設(shè)我們確信并想將我們的2個(gè)模塊轉(zhuǎn)換成一個(gè)很好的包。 要做到這一點(diǎn),我們需要?jiǎng)?chuàng)建一個(gè)包含一個(gè)空的__init__.py文件的目錄,并將我們的文件移入該目錄: 就是這樣 - 現(xiàn)在你有一個(gè)比薩餅包!
請(qǐng)記住,程序包是模塊的名稱空間,因此您不會(huì)導(dǎo)入包本身,而是從包中導(dǎo)入模塊。 如果以這種方式進(jìn)行導(dǎo)入,則可能看起來(lái)過(guò)于冗長(zhǎng),因?yàn)槟枰褂猛耆薅ǖ拿Q。 我猜這是有意為之,因?yàn)镻ython宗旨之一是“明確比隱含更好”。 無(wú)論如何,你總是可以使用from package import module的格式來(lái)縮短名稱: 包初始化 還記得我們?nèi)绾伟岩粋€(gè)__init__.py文件放在一個(gè)目錄中,這個(gè)目錄就神奇地變成了一個(gè)包嗎?這是一個(gè)很好的慣例配置示例,我們不需要描述任何配置或注冊(cè)任何東西。約定包含__init__.py的任何目錄都是Python包。 除了標(biāo)識(shí)一個(gè)包,__init__.py還有一個(gè)目的 - 包初始化。這就是為什么它被稱為init!初始化是在包導(dǎo)入時(shí)觸發(fā)的,換句話說(shuō),導(dǎo)入包時(shí)調(diào)用__init__.py
在__init__模塊中,你可以做任何你想做的事情,但最常用的是用于一些包初始化或設(shè)置專用的__all__變量。后者控制*(通配符)導(dǎo)入 - from package import *。 而且因?yàn)镻ython很棒,我們可以在__init__模塊中做很多事情,甚至是很奇怪的事情。假設(shè)我們不喜歡顯式導(dǎo)入,并且希望將所有模塊符號(hào)上升到包級(jí)別,這樣我們就不必記住實(shí)際的模塊名稱。 為此,我們可以在__init__.py中像這樣導(dǎo)入menu和pizza模塊中的所有東西 看看運(yùn)行結(jié)果: 沒(méi)有更多的pizzapy.menu.Menu或menu.MENU :-)這種方式有點(diǎn)像Go中的軟件包,但請(qǐng)注意,你正試圖濫用Python,不鼓勵(lì)這樣做,因?yàn)樵谀阋a檢查時(shí),會(huì)讓你抓狂的。 別怪我哦,我只是為了舉例說(shuō)明! 您可以像這樣更簡(jiǎn)潔地重寫(xiě)導(dǎo)入 這只是另一種做同樣事情的語(yǔ)法,就是所謂的相對(duì)導(dǎo)入。 我們來(lái)仔細(xì)看看。 絕對(duì)和相對(duì)導(dǎo)入 上面的2個(gè)代碼段是做所謂的相對(duì)導(dǎo)入的唯一方法,因?yàn)樽訮ython 3開(kāi)始,所有導(dǎo)入都默認(rèn)為絕對(duì)導(dǎo)入(如在PEP328中),這意味著導(dǎo)入將嘗試首先導(dǎo)入標(biāo)準(zhǔn)模塊,然后才導(dǎo)入本地包。 在創(chuàng)建自己的sys.py模塊時(shí),需要避免使用標(biāo)準(zhǔn)模塊的名稱,因?yàn)閕mport sys可以覆蓋標(biāo)準(zhǔn)庫(kù)sys模塊。
但是如果你的軟件包有一個(gè)名為sys的模塊,并且你想把它導(dǎo)入到同一個(gè)包內(nèi)的另一個(gè)模塊中,你必須做相對(duì)的導(dǎo)入。 要做到這一點(diǎn),你必須再次明確的這樣寫(xiě)package.module import somesymbol或from .module import somesymbol。 模塊名稱之前的那個(gè)有趣的點(diǎn)理解為“當(dāng)前包”。
可執(zhí)行程序包 在Python中,您可以使用python3 -m 然而也可以這樣調(diào)用:
如你所看到的,這需要一個(gè)__main__模塊,因此要先實(shí)現(xiàn)它:
現(xiàn)在可以正常使用了:
導(dǎo)入兄弟包 而我想要涵蓋的最后一件事是導(dǎo)入兄弟包。 假設(shè)我們有一個(gè)兄弟包pizzashop:
現(xiàn)在,位于頂層目錄下,如果我們?cè)噲D像這樣調(diào)用shop.py
我們得到了找不到pizzapy模塊的錯(cuò)誤。 但是,如果我們把它作為包的一部分來(lái)調(diào)用它
它能正常工作了。 這到底是怎么回事? 對(duì)此的解釋原因在于Python模塊的搜索路徑,在模塊文檔中有很詳細(xì)的描述。 模塊搜索路徑是解釋器用于查找模塊的目錄(在運(yùn)行時(shí)可用sys.path得到)的列表。 它通過(guò)Python標(biāo)準(zhǔn)模塊(/usr/lib64/python3.6)的路徑進(jìn)行初始化,site-packages是pip放置全局安裝的所有內(nèi)容的地方,也是一個(gè)依賴如何運(yùn)行模塊的目錄。 如果將模塊像這樣python3 pizzashop/shop.py作為一個(gè)文件運(yùn)行,則將包含目錄(pizzashop)的路徑添加到sys.path中。 另外,使用-m選項(xiàng)運(yùn)行時(shí),當(dāng)前目錄(如在pwd中)被添加到模塊搜索路徑。 我們可以通過(guò)在pizzashop/shop.py中打印sys.path來(lái)檢查它:
正如你在第一種情況中可以看到的,我們?cè)诼窂街杏衟izzashop dir,所以我們找不到兄弟包pizzapy,而在第二種情況下,當(dāng)前dir(表示為'')在sys.path中并且包含兩個(gè)包。
當(dāng)人們將一堆測(cè)試或示例腳本放在主包相鄰的目錄或包中時(shí),常常會(huì)出現(xiàn)導(dǎo)入同級(jí)包的問(wèn)題。 這里有幾個(gè)StackOverflow問(wèn)題: https:///q/6323860 https:///q/6670275 好的解決方案是把測(cè)試或例子放在包里,然后使用相對(duì)的導(dǎo)入來(lái)避免這個(gè)問(wèn)題。 差點(diǎn)的解決方案是在運(yùn)行時(shí)修改sys.path,增加所需包的父目錄(耶,動(dòng)態(tài)?。?。 人們實(shí)際上這樣做,雖然這是一個(gè)糟糕的方式。 結(jié)束語(yǔ) 我希望閱讀這篇文章之后,你將會(huì)對(duì)Python的導(dǎo)入有更好的理解,并且可以最終順利地將你工具箱中的巨大腳本分解成多個(gè)部分。最后,Python中的所有東西都非常簡(jiǎn)單,即使它不能完整地滿足你的需求,你總可以在運(yùn)行時(shí)隨時(shí)修改任何內(nèi)容。 |
|
|
來(lái)自: 長(zhǎng)沙7喜 > 《編程》