施工專題第11篇:Python 包和模塊使用總結(jié)

4列表專題
Python 模塊
今天這個專題討論P(yáng)ython代碼工程化、結(jié)構(gòu)化的方法。我們都會遇到這種情景:所有代碼都堆積到一個模塊里,導(dǎo)致代碼越來越長,最后變得難以維護(hù),很明顯代碼只寫到一個py模塊文件是不可取的。如何按照邏輯功能,將代碼劃分到不同模塊,組織為一個更易讀、更易維護(hù)的代碼結(jié)構(gòu)呢?歡迎學(xué)習(xí)這個專題。
1 包和模塊的定義
包(package)是一個文件夾,它里面會有一個__init__.py,還有我們自己定義的.py文件。
而我們自己定義的.py文件,python中稱為模塊(module),一個模塊就是一個py文件,里面封裝了一個功能模塊,可能有函數(shù)、類、變量等。
如下建立的一個代碼結(jié)構(gòu):
classdemo/
├──?animals
│???├──?animal2.py
│???├──?animal.py
│???├──?__init__.py
│???├──?manager2.py
│???└──?manager.py
└──?search
????├──?binarytree_level.py
????└──?__init__.py
里面包括兩個package,一個為animals包,另一個為search包. 每個package里都有一個__init__.py文件。
使用這種結(jié)構(gòu)帶來什么便利?每個模塊間的變量又該如何引用?里面的__init__.py起到什么作用?下面一一解答。
2 解決變量命名沖突
對程序員而言,變量命名往往是一個很頭疼的難題,并且一不小心就會寫出名稱相同的變量,尤其是在同一模塊里變量名稱重復(fù)會很麻煩。
通常來說,一個模塊里定義的代碼行數(shù)不要太多,盡量拆分到不同的模塊里,不同的模塊允許出現(xiàn)相同名稱的變量,這是劃分不同模塊的作用之一。
但是僅有模塊好像還不夠,對于一個大點(diǎn)的框架,再按照大的功能邏輯劃分出包(package)顯得更有必要。
并且有了package后,相同變量名字沖突的可能性會更小。如第1小節(jié)中的Animal類,它的完整名稱實(shí)際為:animals.animal2.Animal,這樣在使用Animal等類時,導(dǎo)入方法是下面這樣:
from?animals.animal2?import?(Animal2,Cat,Bird)
實(shí)際上,這種層級的組織在一些大的框架中到處可見。
3 __init__.py 作用
如上所述,__init__.py會使得普通的文件夾變?yōu)閜ackage. 實(shí)際上,__init__.py也是一個模塊,其名稱正是package的名字。
一般來說此文件為空,如下導(dǎo)入animals包:
In?[2]:?import?animals??????????????????????????????????????????????????????????????????????????????
In?[3]:?animals???????????????????????????????????????????????????????????????????????????????????????
Out[3]:?'animals'?from?'/home/zglg/mywork/mdfiles/classdemo/animals/__init__.py'
可以看到導(dǎo)入animals包實(shí)際上導(dǎo)入了它下面的__init__.py文件。
同時還可以為它增加其他功能。
因為在導(dǎo)入一個包時實(shí)際上導(dǎo)入它的__init__.py文件,利用此特性,可以在__init__.py文件中批量導(dǎo)入多個模塊都在公用的模塊,而不再需要一個一個的導(dǎo)入。
拿上面的demo來說,manager.py和manager2.py中都用到time模塊,我們就其移動到__init__.py里:
#?__init__.py
import?time
import?os
import?sys
import?abc
在使用這些內(nèi)置等模塊時,首先導(dǎo)入包:
import?animals?#?導(dǎo)入包
在調(diào)用time模塊時,必須使用包名+模塊名的方式引用:
def?recordTime(self):
????????#引用變?yōu)椋喊鸻nimals +?模塊名稱
????????self.__t?=?animals.time.time()?
????????print('feeding?time?for?%s?is?%.0f'%(self.animal.name,self.__t))
????????self.animal.getSpeedBehavior()
__init__.py文件還有一個功能,通過設(shè)置__all__列表,指定導(dǎo)出的變量、函數(shù),只有在__all__列表里的變量才能被其他模塊所引用。4 解決找不到模塊的問題
我們知道Python中使用import導(dǎo)入需要的包,然而平時使用像vscode, pycharm這類ide時,經(jīng)常出現(xiàn)找不到包的問題,錯誤信息如下:
Exception?has?occurred:?ModuleNotFoundError
No?module?named?'animals'
要想解決此問題,需要首先了解Python的import機(jī)制。
當(dāng)導(dǎo)入模塊時,解釋器會按照sys.path列表中的目錄順序來查找導(dǎo)入文件。要想查看解釋器目前查找的目錄順序,先導(dǎo)入通過sys模塊,使用sys.path,如下是import時查看的目錄順序:
['/home/zglg/mywork/md...mo/animals',
'/home/zglg/anaconda3...thon37.zip',
'/home/zglg/anaconda3.../python3.7',
'/home/zglg/anaconda3...ib-dynload',
'/home/zglg/anaconda3...e-packages']
看到animals包不在解釋器要查找的目錄里,所以出錯了。
所以需添加animals包所在的文件夾路徑,其中一種修改方法如下,直接粗暴向sys.path中添加找不到的目錄:
#?調(diào)整為根目錄(調(diào)用dirname一次獲得其所在文件夾)?
#?就當(dāng)前文件目錄,我們兩次便定位到根目錄?classdemo
BASE_DIR?=?os.path.dirname(os.path.dirname(os.path.abspath(__file__)))??
#?__file__獲取執(zhí)行文件相對路徑,整行為取上一級的上一級目錄
sys.path.append(BASE_DIR)
import?animals
再次啟動程序,看到animals包目錄已經(jīng)顯示搜索path列表中:
['/home/zglg/mywork/mdfiles/classdemo',
'/home/zglg/mywork/md...mo/animals',
'/home/zglg/anaconda3...thon37.zip',
'/home/zglg/anaconda3.../python3.7',
'/home/zglg/anaconda3...ib-dynload',
'/home/zglg/anaconda3...e-packages']
接下來就可以正常導(dǎo)入animals包,找不到包的問題解決。
以上就是此專題介紹的Python包、模塊概念,以及如何應(yīng)用到我們自己的實(shí)際項目的代碼框架中,寫出更加容易維護(hù)、可讀性更好的代碼。
《end》
? ?【送書福利】:送4本阿里新出的書籍電商算法實(shí)戰(zhàn):
點(diǎn)擊圖片,查看詳情
【送書方法】:綜合留言質(zhì)量、近期公眾號參與度等,截止8月8日晚。原創(chuàng)不易,歡迎點(diǎn)贊或轉(zhuǎn)發(fā),這樣我會有更大動力寫好下一篇。

