Python類、模組、包的區別

工程師WWW發表於2017-04-19

類的概念在許多語言中出現,很容易理解。它將資料和操作進行封裝,以便將來的複用。

模組

模組,在Python可理解為對應於一個檔案。在建立了一個指令碼檔案後,定義了某些函式和變數。你在其他需要這些功能的檔案中,匯入這模組,就可重用這些函式和變數。一般用module_name.fun_name,和module_name.var_name進行使用。這樣的語義用法使模組看起來很像類或者名字空間,可將module_name 理解為名字限定符。模組名就是檔名去掉.py字尾。

client.py

def func():
    print "hello world!"

 

main.py

import client
if __name__ == '__main__':
    print __name__
    client.func()
    print client.__name__
 

>>python main.py  ---> result:

__main__

hello world!

client

模組屬性__name__,它的值由Python直譯器設定。如果指令碼檔案是作為主程式呼叫,其值就設為__main__,如果是作為模組被其他檔案匯入,它的值就是其檔名。

每個模組都有自己的私有符號表,所有定義在模組裡面的函式把它當做全域性符號表使用。

模組可以匯入其他的模組。通常將import語句放在模組的開頭,被匯入的模組名字放在匯入它的模組的符號表中。

from module import names  可以直接從模組中匯入名字到符號表,但模組名字不會被匯入。
from module import *     可以把模組中的所有名字全部匯入,除了那些以下劃線開頭的名字元號。不建議使用,不清楚匯入了什麼符號,有可能覆蓋自己定義的東西
 
內建函式dir()可以檢視模組定義了什麼名字(包括變數名,模組名,函式名等):dir(模組名),沒有引數時返回所有當前定義的名字

模組搜尋路徑

當匯入一個模組時,直譯器先在當前包中查詢模組,若找不到,然後在內建的built-in模組中查詢,找不到則按sys.path給定的路徑找對應的模組檔案(模組名.py)
sys.path的初始值來自於以下地方:
    包含指令碼當前的路徑,當前路徑
    PYTHONPATH
    預設安裝路徑
sys.path初始化完成之後可以更改
 
編譯過的Python檔案: .pyc檔案
    
 
built-in 模組

上面的例子中,當client被匯入後,python直譯器就在當前目錄下尋找client.py的檔案,然後再從環境變數PYTHONPATH尋找,如果這環境變數沒有設定,也不要緊,直譯器還會在安裝預先設定的的一些目錄尋找。這就是在匯入下面這些標準模組,一切美好事情能發生的原因。

這些搜尋目錄可在執行時動態改變,比如將module1.py不放在當前目錄,而放在一個冷僻的角落裡。這裡你就需要通過某種途徑,如sys.path,來告知Python了。sys.path返回的是模組搜尋列表,通過前後的輸出對比和程式碼,應能理悟到如何增加新路徑的方法了吧。非常簡單,就是使用list的append()或insert()增加新的目錄。

#module2.py
import sys
import os

print sys.path
workpath = os.path.dirname(os.path.abspath(sys.argv[0]))
sys.path.insert(0, os.path.join(workpath, 'modules'))
print sys.path

其他的要點

模組能像包含函式定義一樣,可包含一些可執行語句。這些可執行語句通常用來進行模組的初始化工作。這些語句只在模組第一次被匯入時被執行。這非常重要,有些人以為這些語句會多次匯入多次執行,其實不然。

模組在被匯入執行時,python直譯器為加快程式的啟動速度,會在與模組檔案同一目錄下生成.pyc檔案。我們知道python是解釋性的指令碼語言,而.pyc是經過編譯後的位元組碼,這一工作會自動完成,而無需程式設計師手動執行。

通常包總是一個目錄,可以使用import匯入包,或者from + import來匯入包中的部分模組。包目錄下為首的一個檔案便是 __init__.py。然後是一些模組檔案和子目錄,假如子目錄中也有 __init__.py 那麼它就是這個包的子包了。

在建立許許多多模組後,我們可能希望將某些功能相近的檔案組織在同一資料夾下,這裡就需要運用包的概念了。包對應於資料夾,使用包的方式跟模組也類似,唯一需要注意的是,當資料夾當作包使用時,資料夾需要包含__init__.py檔案,主要是為了避免將資料夾名當作普通的字串。__init__.py的內容可以為空,一般用來進行包的某些初始化工作或者設定__all__值,__all__是在from package-name import *這語句使用的,全部匯出定義過的模組。

 

可以從包中匯入單獨的模組。
1). import PackageA.SubPackageA.ModuleA,使用時必須用全路徑名
2). 變種: from PackageA.SubPackageA import ModuleA, 可以直接使用模組名而不用加上包字首。
3). 也可以直接匯入模組中的函式或變數:from PackageA.SubPackageA.ModuleA import functionA
 
import語句語法:
1. 當使用from package import item時,item可以是package的子模組或子包,或是其他的定義在包中的名字(比如一個函式、類或變數)
   首先檢查item是否定義在包中,不過沒找到,就認為item是一個模組並嘗試載入它,失敗時會丟擲一個ImportError異常。
2. 當使用import item.subitem.subsubitem語法時,最後一個item之前的item必須是包,最後一個item可以是一個模組或包,但不能是類、函式和變數
 
3. from pacakge import *
   如果包的__init__.py定義了一個名為__all__的列表變數,它包含的模組名字的列表將作為被匯入的模組列表。
   如果沒有定義__all__, 這條語句不會匯入所有的package的子模組,它只保證包package被匯入,然後匯入定義在包中的所有名字。
 
python包是:
包是一個有層次的檔案目錄結構,它定義了由n個模組或n個子包組成的python應用程式執行環境。
通俗一點:包是一個包含__init__.py 檔案的目錄,該目錄下一定得有這個__init__.py檔案和其它模組或子包。
 
 

常見問題:

  • 引入某一特定路徑下的模組

    • 使用sys.path.append(yourmodulepath)
  • 將一個路徑加入到python系統路徑下,避免每次通過程式碼指定路徑

    • 利用系統環境變數 export PYTHONPATH=$PYTHONPATH:yourmodulepath
    • 直接將這個路徑連結到類似/Library/Python/2.7/site-packages目錄下
  • 好的建議

    • 經常使用if __name__ == '__main__',保證你寫包既可以import又可以獨立執行,用於test。
    • 多次import不會多次執行模組,只會執行一次。可以使用reload來強制執行模組,但不提倡。

包(package)

為了組織好模組,將多個模組分為一個包。包是python模組檔案所在的目錄,且該目錄下必須存在__init__.py檔案。常見的包結構如下:

package_a
├── __init__.py
├── module_a1.py
└── module_a2.py
package_b
├── __init__.py
├── module_b1.py
└── module_b2.py
main.py
  • 如果main.py想要引用packagea中的模組modulea1,可以使用:
from package_a import module_a1
import package_a.module_a1
  • 如果packagea中的modulea1需要引用packageb,那麼預設情況下,python是找不到packageb。我們可以使用sys.path.append('../'),可以在packagea中的__init__.py新增這句話,然後該包下得所有module都新增* import __init_即可。

相關文章