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


轉載 :https://blog.csdn.net/u011089523/article/details/52931844

 

本文主要介紹如何在一個Python項目中,優雅的實現項目內各個package的模塊(module)之間的相互引用。

之所以寫這篇文章,是因為網上流傳的各種奇技淫巧簡直五花八門(包括stackoverflow等知名社區),極易誤導對python的import機制不熟悉的人。比如我就曾一度因為找不到優雅的import方式,而認為python是一門愚蠢的語言。所以,我把近一上午的學習結果總結出來,希望大家不要誤入歧途。

本文參考了如下兩篇博客: 
habnab關於python package的精彩總結:地址點我 
Jean-Paul Calderone關於python項目結構的建議:地址點我

demo project

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

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

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

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

其中,package中的文件結構如下圖: 
項目結構

python package

基礎知識

當你import的時候,python只會在sys.path這個變量(一個list,你可以print出來看)里面的路徑中找可能匹配的package和module。

而一個package跟一個普通文件夾的區別在於,package的文件夾中多了一個__init__.py文件。換句話說,如果你在某個文件夾中添加了一個__init__.py文件,則python就認為這個文件夾是一個package。

__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運行一下


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM