用 .pth 檔案附加 Python 模組搜尋路徑
上一篇ofollow,noindex" target="_blank">Python 的模組搜尋路徑
,介紹了 Python 的模組搜尋路徑,最終起作用的是sys.path
路徑列表。如果要自定義自己的搜尋路徑,就是要怎麼定製sys.path
的內容。可以簡單的用PYTHONPATH
環境變數前向新增,這兒將要說的是用.pth
檔案的方式。也可由此進一步理解 Python 依賴管理工具,像 virtualenv
等的工作原理。
.pth
檔名是什麼,無所謂,Python 只認副檔名。.pth
檔案中每行指定一個路徑 -- 絕對或相對路徑(相對於本 .pth
檔案所在的目錄),另外還可以空行或#
開始的註釋行,還能有import
語句,大概只用來校驗是否能匯入成功,程式程式碼中還是需要顯示的 import 模組。
.pth
檔案放在哪裡
.pth
檔案建立好後應該放到哪裡去呢?不是sys.prefix
指示的位置,也不是sys.path
中任意一個目錄,而是 sys.path
中屬於 site.packages 的某一個目錄中。可以用
>>> import site>>> site.getusersitepackages()>>> site.getsitepackages()
檢視到, 看我在 Ubuntu Linux 中看到的內容(為便於閱讀,顯示列表內容時進行了換行處理)
>>> import sys, site >>> sys.path ['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/home/yanbin/.local/lib/python3.6/site-packages', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages', '/usr/lib/python3.6/dist-packages'] >>> site.getusersitepackages() '/home/yanbin/.local/lib/python3.6/site-packages' >>> site.getsitepackages() ['/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages', '/usr/lib/python3.6/dist-packages'] >>> site.USER_SITE '/home/yanbin/.local/lib/python3.6/site-packages' >>> site.ENABLE_USER_SITE True
從上面看到sys.path
包含了site.getusersitepackages()
和site.getsitepackages()
的內容,並且site.getusersitepackages()
在前
'/home/yanbin/.local/lib/python3.6/site-packages','/usr/local/lib/python3.6/dist-packages','/usr/lib/python3/dist-packages','/usr/lib/python3.6/dist-packages'
而我們的.pth
檔案就可以放到以上四個目錄中的任意位置,也可以放置多個.pth
檔案,選擇那個目錄,一個是搜尋的先後順序,還有就是.pth
中的相對目錄會相對於.pth
所在的位置。
如果是不考慮多使用者共享的情況,並且操作不用sudo
字首,我們就把.pth
檔案放到usersitepackages()
指示的目錄下,即/home/yanbin/.local/lib/python3.6/site-packages
。該目錄還能直接用如下命令獲得得
python3 -m site --user-site/home/yanbin/.local/lib/python3.6/site-packages
在此位置上建立一個檔案,命名為example.pth
, 內容如下
/home/program/mymath mymodules
第一行用的絕對目錄,第二行為相對目錄,相對於example.pth
所在的目錄/home/yanbin/.local/lib/python3.6/site-packages
。假如以上兩個目錄
/home/program/mymath/home/yanbin/.local/lib/python3.6/site-packages/mymodules
都存在的話,進到 Python3 Shell, 再次檢視sys.path
的內容就變成了
>>> import sys, site >>> sys.path ['', '/usr/lib/python36.zip', '/usr/lib/python3.6', '/usr/lib/python3.6/lib-dynload', '/home/yanbin/.local/lib/python3.6/site-packages', '/home/program/mymath', '/home/yanbin/.local/lib/python3.6/site-packages/mymodules', '/usr/local/lib/python3.6/dist-packages', '/usr/lib/python3/dist-packages', '/usr/lib/python3.6/dist-packages']
留意上面的順序,.pth
放在哪個目錄下,它所增加的路徑就會跟隨在那個 site-packages 後面。效果上相當於
pos = sys.path.index('/home/yanbin/.local/lib/python3.6/site-packages')sys.path[pos:pos] = ['home/program/mymath', '/home/yanbin/.local/lib/python3.6/site-packages/mymodules']
如果example.pth
檔案放在/usr/lib/python3/dist-packages
目錄中,那麼新增的兩個路徑也就跟在它後面,並且相對路徑也是相對它。
Python 會話啟動時會對.pth
中配置的目錄存在性進行檢測,如果example.pth
中配置的/home/program/mymath
不存在,那麼/home/program/mymath
將不會出現在 sys.path
列表中, 相對路徑也是如此。
.pth
檔案中import
的作用
再來稍微探討一下在.pth
檔案中import
語句的作用,.pth
中除了空行,註釋行和目錄外,只能有 import
開頭的語句。實踐中試驗了一下,在 .pth
中寫上一句
import mymath
並不意味著在程式程式碼中就能直接用 mymath 模組了,若直接mymath.pi
使用mymath
中的屬性或方法是會收到
NameErro: name 'mymath' is not defined
的錯誤,因此儘管在example.pth
中有 import mymath
, 在程式程式碼中欲使用mymath
模組的話還必須加上同樣的import mymath
。
好像.pth
中的import mymath
語句沒什麼用,卻又並不屬實。比如說在 .pth
中import mymathxx
不存在的模組,在進入 Python3 會話時就會收到一個錯誤
➜ ~ python3Error processing line 4 of /home/yanbin/.local/lib/python3.6/site-packages/example.pth: Traceback (most recent call last):File "/usr/lib/python3.6/site.py", line 174, in addpackageexec(line)File "<string>", line 1, in <module>ModuleNotFoundError: No module named 'mymathxx' Remainder of file ignored
不能載入到模組mymathxx
, 並且import mymathxx
後的內容被忽略掉,仍能進入 Python3 的 Shell。
所以粗略的感覺.pth
中的import
的語句只是用來校驗想要載入的模組是否真的存在。當有任何欲載入的模組不能匯入的話應當給予重視。
pipenv
和virtualenv
中的路徑初探
pipenv
和 virtualenv
環境中的site
沒有了 site.getsitepackages()
和site.getusersitepackages()
這兩個方法了。
pipenv
pipenv shell
,然後再進入 python3
或者用 pipenv run python3 main.py
來檢視sys.path
的內容,類似如下:
'/home/yanbin/myproject' '/home/yanbin/.local/share/virtualenvs/myproject-iMKbKEIX/lib/python36.zip' '/home/yanbin/.local/share/virtualenvs/myproject-iMKbKEIX/lib/python3.6' '/home/yanbin/.local/share/virtualenvs/myproject-iMKbKEIX/lib/python3.6/lib-dynload' '/usr/lib/python3.6' '/home/yanbin/.local/share/virtualenvs/myproject-iMKbKEIX/lib/python3.6/site-packages'
virtualenv
如果是一個virtualenv
專案的話,它目錄下有自己獨套班子,bin
,include
,lib
目錄,bin
下有自己的 python3, pip3 等命令。執行專案自己的 python3
檢視sys.path
內容大致如下:
['', '/home/yanbin/myproject/lib/python36.zip', '/home/yanbin/myproject/lib/python3.6', '/home/yanbin/myproject/lib/python3.6/lib-dynload', '/usr/lib/python3.6', '/home/yanbin/myproject/lib/python3.6/site-packages']
幾乎所有的依賴都能在專案目錄下找到(除 /usr/lib/python3.6 外),不用跑到使用者目錄或 Python 公共的 site-packages 目錄去找。因此virtualenv
專案才是一個獨立可部署的,打包,拷貝,解壓後執行自己的 bin/Python3 命令即可。
連結: