1. python3中的模塊和包
簡單來講,模塊是一個包含Python定義和語句的文件,包是一種通過用“帶點號的模塊名”來構造 Python 模塊命名空間的方法。
詳細可以參考廖雪峰的:python教程-模塊
本文以以下的項目結構,對python3的子包之間的調用進行探索:
使用的python版本:3.7.0
平台:win10
2. python的模塊搜索路徑
當一個名為 spam 的模塊被導入的時候,解釋器首先尋找具有該名稱的內置模塊。如果沒有找到,然后解釋器從 sys.path 變量給出的目錄列表里尋找名為 spam.py 的文件。sys.path 初始有這些目錄地址:
- 包含輸入腳本的目錄(或者未指定文件時的當前目錄);
- PYTHONPATH (一個包含目錄名稱的列表,它和shell變量 PATH 有一樣的語法);
- 取決於安裝的默認設置;
3. 調用子包與調用兄弟包
假如在main.py中調用pack.mod1.py中的函數func1,而func1又調用pack2.mod2.py中的func2函數,各個文件中的代碼如下:
main.py:
import sys print("file:{},sys.path:{}".format(__file__, sys.path)) from pack1 import mod1 if __name__ == "__main__": mod1.func1()
pack1/mod1.py:
from pack2 import mod2 def func1(): print("this is pack1.mod1") mod2.func2()
pack2/mod2.py:
def func2(): print("this is pack2.mod2")
進入test_mod目錄,執行命令:
python main.py
輸出:
file:main.py,sys.path:['D:\\1-Work\\python_src\\test_mod',
'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\python37.zip', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37', 'C:\\Users\\xxx\\AppData\\Roaming\\Python\\Python37\\site-packages', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site-packages'] this is pack1.mod1 this is pack2.mod2
可以看出sys.path中的第一個元素,就是main.py所在的目錄,因為包pack1和pack2都在這個目錄下,所以它們能夠互相發現。
假如這個時候,你想單獨調試一下pack1.mod1.func1函數,將mod1.py修改如下:
import sys print("file:{},sys.path:{}".format(__file__, sys.path))
from pack2 import mod2 def func1(): print("this is pack1.mod1") mod2.func2() if __name__ == "__main__": func1()
進入pack1目錄運行:
python mod1.py
輸出如下:
file:mod1.py,sys.path:['D:\\1-Work\\python_src\\test_mod\\pack1',
'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\python37.zip', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37', 'C:\\Users\\xxx\\AppData\\Roaming\\Python\\Python37\\site-packages', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site-packages'] Traceback (most recent call last): File "mod1.py", line 3, in <module> from pack2 import mod2 ModuleNotFoundError: No module named 'pack2'
發現sys.path的第一個路徑已經是test_mod/pack1了,在這個目錄下面解釋器無法找到pack2這個包。
如果需要發現這個包,按照python搜索路徑的規則,需要將test_mod這個目錄加入到sys.path中,一般來說可以:
- 通過環境變量PYTHONPATH進行設置;
- 也可以將路徑寫入.pth文件,Python在遍歷已知的庫文件目錄過程中,如果見到一個.pth 文件,就會將文件中所記錄的路徑加入到 sys.path 中;
- 最簡單的是使用sys.path.append函數將路徑加入。
使用最后一種辦法,將pack1.mod1.py修改為:
import sys sys.path.append("D:\\1-Work\\python_src\\test_mod") print("file:{},sys.path:{}".format(__file__, sys.path)) from pack2 import mod2 def func1(): print("this is pack1.mod1") mod2.func2() if __name__ == "__main__": func1()
結果輸出:
file:mod1.py,sys.path:['D:\\1-Work\\python_src\\test_mod\\pack1', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\python37.zip', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\DLLs', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\lib', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37', 'C:\\Users\\xxx\\AppData\\Roaming\\Python\\Python37\\site-packages', 'C:\\Users\\xxx\\AppData\\Local\\Programs\\Python\\Python37\\lib\\site-packages', 'D:\\1-Work\\python_src\\test_mod']
為什么使用絕對路徑呢,因為如果使用相對路徑的話,當前工作路徑一變就不行了,不信在test_mod目錄執行
python pack1/mod1.py
或者用vscode等文本編輯器打開test_mod目錄,然后直接F5運行,工作目錄就是test_mod,而不是pack1,這個時候添加的”../”路徑就會變成目錄python_src。
這也是為什么很多文章使用sys.path.append(“../”),然后很多人在留言區說在他們本地運行的時候根本不行。
所以一切的關鍵還是模塊搜索路徑。
回到解決方式上面,或者將項目的路徑加到PYTHONPATH環境變量,或者寫入.pth並放在搜索路徑中才是一勞永逸的方式。
4. 參考
(1) python3文檔-模塊
(2) 廖雪峰:python教程-模塊
(完)

