python項目內import其餘內部package的模塊的正確方法

轉載 :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

本文以一個demo project爲例,來介紹python的包管理機制。網絡

這個demo project我放到github上了:地址點我app

其中,項目根目錄有三個文件夾:函數

  • data:存放項目數據
  • doc:存放項目文檔
  • package:一個demo python package

其中,package中的文件結構以下圖: 
項目結構學習

python 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中某個文件夾下)。

錯誤的import作法

如上述project中,若是你想讓subpackage2中的foo2來import subpackage1中的foo1,便會出現找不到subpackage1的狀況。

目前網絡上大部分的作法都是經過sys.path.append(yourpath)之類的方法,將你須要import的module的目錄添加到sys.path中。或者,經過修改PYTHONPATH這個環境變量來將添加(跟修改sys.path效果相同)。

可是,這種作法有以下幾個缺點:

  • 若是你用PYTHONPATH,那麼當有多個項目時,你須要把每一個項目的根目錄都加入到PYTHONPATH中,會使得PYTHONPATH變得十分臃腫
  • 若是你使用sys.path,因爲文件夾是動態添加的,因此當你使用相對路徑的時候,實際路徑會十分依賴於你的入口函數,當入口函數改變極可能就會致使代碼沒法運行
  • 若是你使用絕對路徑,將你的代碼在其餘機器上運行的時候須要從新配置這些變量,十分麻煩

正確的作法

代碼中正常import

首先,在代碼中按照正常方式導入你須要的包

好比,你須要在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的情景)。

建立__main__.py文件

在package的根目錄中建立__main__.py文件,可使得你的package能夠經過python -m直接運行。

demo中的__main__.py文件十分簡單:

 

from package.app import main main()


即import真正的主函數app.py中的main方法,而後調用main()

用python -m運行你的python文件

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()

總結

至此,咱們已經實現了你所但願的全部功能:

  • 在project內部實現各個模塊之間的import
  • project中的各個.py文件能夠直接運行
  • 將project遷移到其餘機器時,不用進行額外配置

若是還有不明白的,能夠將github上的源碼下載下來看一看,而後用python -m運行一下

相關文章
相關標籤/搜索