轉載 :https://blog.csdn.net/u011089523/article/details/52931844html
本文主要介紹如何在一個Python項目中,優雅的實現項目內各個package的模塊(module)之間的相互引用。python
之因此寫這篇文章,是由於網上流傳的各類奇技淫巧簡直五花八門(包括stackoverflow等知名社區),極易誤導對python的import機制不熟悉的人。好比我就曾一度由於找不到優雅的import方式,而認爲python是一門愚蠢的語言。因此,我把近一上午的學習結果總結出來,但願你們不要誤入歧途。git
本文參考了以下兩篇博客:
habnab關於python package的精彩總結:地址點我
Jean-Paul Calderone關於python項目結構的建議:地址點我github
本文以一個demo project爲例,來介紹python的包管理機制。網絡
這個demo project我放到github上了:地址點我app
其中,項目根目錄有三個文件夾:函數
其中,package中的文件結構以下圖:
學習
當你import的時候,python只會在sys.path這個變量(一個list,你能夠print出來看)裏面的路徑中找可能匹配的package和module。spa
而一個package跟一個普通文件夾的區別在於,package的文件夾中多了一個__init__.py文件。換句話說,若是你在某個文件夾中添加了一個__init__.py文件,則python就認爲這個文件夾是一個package。.net
__init__.py文件能夠是空的(也推薦者這麼作),它只是告訴python當前文件夾是一個package。固然,也能夠在裏面添加一些代碼,這些代碼會在import這個包的時候運行。
因此,請確保你要import的文件所在的文件夾有__init__.py文件(除非它在sys.path中某個文件夾下)。
如上述project中,若是你想讓subpackage2中的foo2來import subpackage1中的foo1,便會出現找不到subpackage1的狀況。
目前網絡上大部分的作法都是經過sys.path.append(yourpath)之類的方法,將你須要import的module的目錄添加到sys.path中。或者,經過修改PYTHONPATH這個環境變量來將添加(跟修改sys.path效果相同)。
可是,這種作法有以下幾個缺點:
首先,在代碼中按照正常方式導入你須要的包
好比,你須要在app.py中導入foo1,則:
from package.subpackage1 import foo1
雖然你可能發現from subpackage1 import foo1也能夠正常運行,可是請避免這種使用相對路徑的方法。由於這在python3中將再也不支持,同時也有可能會引發奇怪的問題。同時,雖然PEP 328中也給出了 from .subpackage1 import foo1這樣的形式,可是仍是不要本身給本身製造麻煩,統一使用完整路徑(絕對路徑)爲好。
再好比,若是你須要在foo2.py中導入foo1.py(在不一樣的subpackage中),則:
from package.subpackage1 import foo1
跟上面如出一轍,這就是使用絕對路徑的好處,各處的引用高度統一。同時,若是你的package被安裝在其餘用戶的機器中,其餘用戶也會使用這種絕對路徑來import你package中的模塊(回想你本身import第三方package的情景)。
在package的根目錄中建立__main__.py文件,可使得你的package能夠經過python -m直接運行。
demo中的__main__.py文件十分簡單:
from package.app import main main()
即import真正的主函數app.py中的main方法,而後調用main()
python的-m參數官方說法是:
Searches sys.path for the named module and runs the corresponding .py file as a script.
在下面的例子中,加上-m參數後,所運行的.py文件便會識別其頂層的package
回到剛纔的例子。建立完__main__.py以後,cd到項目的根目錄,運行
python -m package
便可實現直接運行__main__.py,即直接運行了package這個包
若是你想直接運行package內的某個.py文件,好比foo1,則:
python -m package.subpackage1.foo1
固然,你要確保foo1中存在判斷其是不是入口函數的邏輯,以下:
if __name__ == "__main__": speak()
至此,咱們已經實現了你所但願的全部功能:
若是還有不明白的,能夠將github上的源碼下載下來看一看,而後用python -m運行一下