1. __init__.py
1.1 什么是__init__.py
在Python3工程里,當python3檢測到一個目錄下存在__init__.py文件時,Python3就會把它當成一個模塊(module)。__init__.py可以是一個空文件,也可以有內容。
1.2 為什么需要__init__.py
使用Python3模塊常見的情況是,在使用A.py時,需要import B.py,需要先拷貝到當前目錄,然后再import,這樣的做法在程序量較小的情況下是可行的,如果程序交互復雜程度稍高,就會復雜費力。有一種解決方法可以將多個.py文件組織起來,方便在外部統一調用以及在內部互相調用。
Python3中的__init__.py在包調用中起到了重要的作用。首先要明確Python3在執行import包的時候,執行的操作,按照Python3的文檔描述,操作如下:
- 創建一個新的module對象(它可能包含多個module);
- 把這個module對象插入sys.module中;
- 裝載module的代碼(首先需要找到module程序所在的位置,其原理為:如果需要導入的module的名字是m1,則解釋器必須找到m1.py,它首先在當前目錄查找,然后是在環境變量PYTHONPATH中查找)
- 執行新的module中對應的代碼。
一個簡單的package示例如圖所示。
其中__init__.py為空,解釋器對其視作一個package處理。
第一層:pack1, pack2, main.py
第二層:pack1: __init__.py,module_A.py
pack2:__init__.py,module_B.py
當main.py調用腳本from pack1 import Module_A,由於from操作,會默認目錄下存在pack1包,解釋器會首先尋找__init__.py。也就是說,package內module的import是受__init__.py限制的。
2. Python3.3+的__init__.py
Python 3.3+版本以后,Python3具有隱式命名空間包,允許它創建不帶__init__.py文件的包,允許隱式名稱空間包意味着可以完全放棄提供__init__.py文件的要求,並使其不受到影響。所以這就是在Python3.3以后的版本中,有時候工程中忘記加入了__init__.py文件而工程仍然能夠正常運行的原因。
3. Python3中的PYTHONPATH
PYTHONPATH是Python3中一個重要的環境變量,用於在導入模塊的時候搜索路徑。因為PYTHONPATH是Python3的搜索路徑,所以默認我們import的模塊都會從PYTHONPATH里面進行尋找。如果我們使用PYTHONPATH中的modules,那么在運行python3前,就要把path加到os.environ['PYTHONPATH'],如果在運行python3后再加,那些模塊不能直接被導入。
可以看到,路徑列表的第一個元素為相對路徑下的當前目錄。由於在導入模塊的時候,解釋器會按照列表的順序搜索,直到找到第一個模塊,所以優先導入的模塊為同一目錄下的模塊。導入模塊時搜索路徑的順序也可以改變,分兩種情況:
- 通過sys.path.append(),sys.path.insert()等方法來改變,這種方法當重新啟動解釋器的時候,原來的設置會失效。
- 改變PYTHONPATH,這種設置方法永久有效。在這種情況下,可以通過在sys.path列表顯示的路徑中添加.pth文件來實現。
4. 若要通過setup.py打包工程時,需要依賴__init__.py
在Python3的PYTHONPATH中,包含Python3安裝目錄相關的一組路徑(內置模塊和標准庫,以及其它第三方模塊的共享路徑),但是它不支持項目所在根目錄這種形式,而是只支持文件所在目錄的相對路徑。以1.2小節中的示例,項目的根路徑為D:\pytest_init,其中包含pack1(Module_A)和pack2(Module_B)包。項目根路徑D:\pytest_init是需要放在PYTHONPATH中的,這樣python解釋器才可以通過包名互相訪問。
而setup.py所做的主要事情:將整個項目添加到PYTHONPATH中,setup.py中所記錄的就是項目模塊添加到PYTHONPATH的規則。setup.py中會定義此項目中有哪些模塊需要被加入到PYTHONPATH,在這個過程中可以把測試項目過濾掉,而對項目中的模塊進行打包時,setup.py會默認該模塊包含並優先加載其中的__init__.py文件,因此在所要加入的每個模塊中,都必須要有__init__.py文件的存在以保證所有需要導入的包都能夠正確被找到並被導入。即,在使用setup.py打包的項目中,__init__.py必須包含在每個模塊包的目錄中,以便自動找到軟件包,這樣打包的軟件包僅在包含__init__.py文件的情況下才能被識別。setup.py中還會定義需要的第三方依賴包,使用安裝命令可以同時安裝這些第三方依賴包等等。