你的 Python 包都裝到哪了?

  • 2019 年 11 月 29 日
  • 筆記

前言

寫這篇文章是因為最近在Python社區看到,有幾個求助頻率非常高的問題:

  • 我安裝了pip為什麼運行報找不到可執行文件?
  • import module為什麼報ModuleNotFound?
  • 為什麼我用Pycharm能運行在cmd里運行不了?

授人以魚不如授人以漁,要解決這類問題,你得知道Python是如何找包的。希望看完這篇文章,能有所幫助。(主要還是下次再有人問,我就可以鏈接甩臉了哈哈)

Python是如何尋找包的

現在大家的電腦上很可能不只有一個Python,還有更多的虛擬環境,導致安裝包的時候,一不小心你就忘記注意安裝包的路徑了。首先我們來解決找包的問題,這個問題回答起來很簡單,但很多人不知道這個原理。假如你的Python解釋器的路徑是<path_prefix>/bin/python,那麼你啟動Python交互環境或者用這個解釋器運行腳本時,會默認尋找以下位置1:

  1. <path_prefix>/lib(標準庫路徑)
  2. <path_prefix>/lib/pythonX.Y/site-packages(三方庫路徑,X.Y是對應Python的主次版本號,如3.7, 2.6)
  3. 當前工作目錄(pwd命令的返回結果)

這裡如果你用的是Linux上的默認Python,<path_prefix>就是/usr,如果你是自己使用默認選項編譯的,<path_prefix>就是/usr/local。從上面第二條可以看到不同次版本號的Python的三方庫路徑不同,如果你把Python從3.6升級到3.7那麼之前裝的三方庫都沒法用了。當然你可以整個文件夾都拷貝過去,大部分情況不會出問題。

幾個有用的函數

  • sys.executable 當前使用的Python解釋器路徑
  • sys.path 當前包的搜索路徑列表
  • sys.prefix 當前使用的<path_prefix>

例:

Python

>>> import sys  >>> sys.executable  '/home/frostming/.pyenv/versions/3.7.2/bin/python'  >>> sys.path  ['', '/home/frostming/.pyenv/versions/3.7.2/lib/python37.zip', '/home/frostming/.pyenv/versions/3.7.2/lib/python3.7', '/home/frostming/.pyenv/versions/3.7.2/lib/python3.7/lib-dynload', '/home/frostming/.local/lib/python3.7/site-packages', '/mnt/d/Workspace/pipenv', '/home/frostming/.pyenv/versions/3.7.2/lib/python3.7/site-packages']  >>> sys.prefix  '/home/frostming/.pyenv/versions/3.7.2'

使用環境變數添加搜索路徑

如果你的包的路徑不存在上面列出的搜索路徑列表裡,可以把路徑加到PYTHONPATH環境變數里,多個路徑用:隔開(Windows用;)。

但需注意,避免把不同Python版本包的路徑加到PYTHONPATH里,比如PYTHONPATH=/home/frostming/.local/lib/python2.7/site-packages,因為PYTHONPATH中的路徑是優先於默認搜索路徑,如果用Python 3的話會有兼容性問題。

順便說下PATH是用來找可執行程式的搜索路徑,假如你在終端中運行命令my_cmd,系統會依次掃描PATH中的路徑,看my_cmd是否存在於該路徑下,所以如果提示找不到程式或命令無法識別,那你就要看路徑是否加到PATH里了。

Python是如何安裝包的

現在用安裝Python包基本是用的pip,就算你是用pipenv,poetry,底層依然是pip,一律適用。 如果你沒有安裝pip請參考這裡,如果安裝了還無法用pip命令請參考上一節。

運行pip有兩種方式:

  • pip ...
  • python -m pip ...

第一種方式和第二種方式大同小異,區別是第一種方式使用的Python解釋器是寫在pip里的,一般情況下,如果你的pip路徑是<path_prefix>/bin/pip,那麼Python路徑對應的就是<path_prefix>/bin/python。第二種方式則顯式地指定了Python的位置。這條規則,對於所有Python的可執行程式都是適用的。流程如下圖所示。

那麼,不加任何自定義配置時,使用pip安裝包就會自動安裝到<path_prefix>/lib/pythonX.Y/site-packages下(<path_prefix>是從上一段里得到的),可執行程式安裝到<path_prefix>/bin下,如果需要在命令行直接使用my_cmd運行,記得加到PATH。

pip中更改安裝位置的選項

  • --prefix PATH,替換<path_prefix>為給定的值
  • --root ROOT_PATH,在<path_prefix>前面加上ROOT_PATH,比如--root /home/frostming,<path_prefix>就會從/usr變成/home/frostming/usr
  • --target TARGET,直接指定安裝位置到TARGET

虛擬環境

虛擬環境就是為了隔離不同項目的依賴包,使他們安裝到不同的路徑下,以防止依賴衝突的問題。理解了Python是如何安裝包的機制之後就不難理解虛擬環境(virtualenv, venv模組)的原理。其實,運行virtualenv myenv會複製一個新的Python解釋器到myenv/bin下,並創建好myenv/lib,myenv/lib/pythonX.Y/site-packages等目錄(venv模組不是用的複製,但結果基本一樣)。執行source myenv/bin/activate以後會把myenv/bin塞到PATH前面,讓這個複製出來的Python解釋器最優先被搜索到。這樣,後續安裝包時,<path_prefix>就會是myenv了,從而實現了安裝路徑的隔離。

總結

看到這裡大家可以發現,關於包路徑搜索最重要的就是這個<path_prefix>路徑前綴,而這個值又是從使用的Python解釋器路徑推導出來的。所以要找到包的路徑,只需要知道解釋器的路徑就可以了,如果遇到改變包的路徑,只需要通過正確的PATH設置,指定你想要的Python解釋器即可。

現在回到開頭的三個問題,大家會解決了嗎?在評論區寫出你的排查步驟或解決方法。

  1. 本文示例均使用Unix路徑習慣,如果是Windows系統則應當做適當改動,如<path_prefix>/bin應為<path_prefix>/Scripts↩

Tags: