撥開 Python, pip, site-packages 的藍色蜘蛛網 💢

Will Wang 王興謙
9 min readOct 13, 2018

--

From: https://xkcd.com/1987/
處理過 Python 環境問題的人看到上圖大概會有一些感覺... 😝

Python 環境問題可能是:

  1. 電腦有超多 Python 版本,有 python, python2.7, python3, python3.4, python3.6, python9527… 每個版本是從哪裡來的最後也搞不清楚,brew, apt-get, yum, anaconda, pip, easy_install, pyenv, OS 自己帶的,自己抓的執行檔,自己抓 tarball 來 build 的。慘的話,可能同樣版本還有不同份在不同 path 上
  2. 電腦有好幾個 pip 版本,有 pip , pip3 , 可能還有 pip3.4, pip3.6, 然後這些 pip 版本自己又有分版本,像是 pip 10.0.1pip 9.0.1 這種。
  3. 不知道 pip 跟 python 的關係是什麼
  4. 不知道 python 在 import 時,dir 尋找順序是什麼
  5. pip install 有時因為權限問題而提示我可以加個--user ,但不懂加了是裝在哪,不加又是裝在哪
  6. 大概知道 python 的 package 好像是裝在 site-packages 這個資料夾,結果後來發現電腦裡有幾百萬個 site-packages
  7. Python 升級之後,裝過的 package 又不知道跑到哪裡去了,一直 ImportError: No module named ‘xxx’ (或 ModuleNotFoundError: No module named ‘xxx’ )

這些問題我常常看到在身邊還有網路上有人問,也都是我以前真實碰過的問題。自己本來是可以用就好,Python 書也不太會把這些解釋得很清楚(書通常是在講 import 邏輯或是 setuptools , distutils 等等)。而最近不想含混過去,就稍微釐清了一下以上問題,然後筆記一下重點,希望讓也想了解的人節省一點時間。

先不討論衍生的東西

然後,光是以上這些問題就算是一個主題了,所以這裡先不試著去討論後來衍生出的 virtualenv, venv, pyenv, pyvenv, virtualenvwrapper, pipenv, pyenv-virtualenvwrapper …,這些東西都是為了解決一些既有問題而生的 tool,其中有幾個可以說是必備。如果有興趣我覺得這篇 Stack Overflow 的整理不錯。

關於 import 邏輯,還有 $PYTHONPATH 這個環境變數,這裡也不贅述了;簡單的話可以直接咕狗,我是建議看書比較系統化跟完整,推薦 精通 Python 3 程式設計Python 參考手冊(Python Essential Reference, 4/e)

幾個簡單的大觀念:

建議用這樣的順序理解下去 ↓

到處都有 site-packages 資料夾 🌏

  1. site-packages 是一個資料夾,放我們安裝的 python package,可能到處都有
  2. 每安裝一個 python 版本,對於版本 X.Y 都有各自的 site-packages
    比方說 python 3.4 跟 python 3.6 都各自有分開的 site-packages 在不同的路徑,而 python 3.6.1 跟 python 3.6.2 的是同一個
  3. 每個 user 可以有自己的 site-packages ,在 home 目錄下的某個地方(如果有興趣可以 google site 這個 package 的 site.USER_SITE,或看這個官方文件(for python 3.7))
  4. ipython 跟一些 tool 裝了之後也有自己的 site-packages

pip / pip3 指令在結構上是什麼? 👭

pip 是個 python script,也是個 python package
  • python script: 我們執行的 pip,只是一個開頭的 python script,裡面會再去 import 電腦裡「某一個」 site-packages 資料夾裡「真正的」 pip package 來跑。(推薦用 which pip 去看一下 script 內容)
  • python package: 承上,至於會 import 到哪一個 site-packages 下的 pip package,是看我們怎麼執行 pip(稍後細說)

pip 版本有兩個層次:

  1. pippip3
    pip 用來管理 python 2.x 用的 package,而 pip3 用來管理 python 3.x 用的 package。他們各自獨立,pip 升級不會變成 pip3,要裝 pip3 也不需要先有 pip。
  2. pip (或 pip3) 有自己本身(pip package)的版本:
    執行 pip --version 可以看到…
pip 10.0.1 from /usr/local/lib/python2.7/site-packages/pip (python 2.7)

這裡的 10.0.1 就是 pip package 的版本 (也可以看到實際上跑到的 pip package 在哪個資料夾)

以上 pip3pip 都是一樣道理,有興趣可以從 which pip3 當作源頭開始追看看 😃

那 python import 時會去看哪些 dir? 📂

想知道是從哪些路徑,用什麼順序去找 package,這裡有兩個方法:

  • 第一個:在 python 裡先 import sys 再看 sys.path 的值
  • 第二個:在 shell 執行 python -m site (或 python3 -m site)

可以觀察一下這兩個方法的輸出,有一些差別。

用不同方式執行 pip ,會 import 到不同資料夾的 pip package 😕

幾種方式的差別:

1. pip
2. python /usr/local/bin/pip
3. python -m pip
  1. 直接執行 pip
    shell 會找到$PATH 裡第一個 pip script,依照這 script 裡的 shebang (就是#!/usr/bin/python 這種東西)來決定用哪個版本的 python 跑,所以會用那個版本 python 的 sys.path 去找 pip package 來用。有興趣的人可以試試去改 shebang 的版本,看會發生什麼事。
  2. python 跑指定路徑的 pip script:
    這樣shell 執行的python 版本會壓過 shebang 指定的 python 版本。比方說,這裡執行的 python 如果是 2.7,就算 shebang 雖然指定要用 python3 跑,最後還是會用 2.7 去跑;所以,這樣就會從 python2.7 的 sys.path 裡去找pip package。
  3. python 的 -m 參數跑 pip
    這樣會去用 shell 執行的 python 版本的 sys.path 裡的pip package(好繞…)。如果 python 指令的版本是 2.6,那就是跑 2.6 裡的 pip package。
    man page 說明:
-m module-name: Searches sys.path for the named module and runs the corresponding .py file as a script.

以上不管是哪一種,如果沒有任何一個 site-packages 裡有 pip,就 import error 跑不起來。

跑了 pip install,那東西到底被裝到哪裡去了? 😕

pip install 會有預設安裝的 dir,也會因為各種參數去改變,像是:
--target, --user, --prefix, --root 等等。

有兩個方法可以看被裝在哪:

  1. 最簡單的方法是剛才講到的,pip --version (或是pip -V)會顯示目前這個 pip 是跑哪一個 site-packages 下的 pip package ,這個路徑就是 install 時會放的 dir
  2. 或,比較間接的方法,先用 pip list 列出所有已經安裝的 pkg,然後用 pip show [某pkg] 就會寫 Location 在哪

如果是用 pip3,方法也是一模一樣。

↑ 以上這些,我覺得是最重要的 😃

其他你可能想知道的:

如何安裝 pip 本身(不是用 pip install 去裝東西)

如果電腦裡沒有 pip 或是需要重新安裝,有好幾種安裝 pip 的方法:

  1. 可以透過 OS 裡 apt/yum這種 package manager
  2. 也可以下載一個 get-pip.py 檔案然後用 python 去執行這個 script。這種就會跟文章前面所敘述的一樣,用不同版本 python 去跑會有差。

升級 pip 本身

pip 可以用自己來升級自己,像是從9.0.1升級到10.0.1

這裡有個很有意思的問題…現在如果要升級 pip3,command 要怎麼下?可能是:

  1. pip3 install --upgrade pip
  2. pip install --upgrade pip3
  3. pip3 instal --upgrade pip3
  4. pip install --upgrade pip

猜猜哪個是對的做法?ㄎㄎ

dist-packages 跟 site-packages 的差別

有時候會看到這兩個詞似乎是可以交換用的,差別在哪?可以看 [這篇],簡略的說是Debain/Ubuntu 為了降低 conflict 的慣例。

Python 環境的問題錯綜複雜,如果有寫錯或是需要更新的,歡迎在留言更正我一下噢! 😃 🙇

有幾位朋友幫忙 review 了這篇文章,感謝!

Reviewer (A-Z): Askeing Yen / Kilik Kuo / Paul Yang / samuelololol / Vincent Liu / Walter Chen

一些關於我的資訊:

--

--

Will Wang 王興謙

Software developer @ Mozilla, Linker Networks, Appier / ex-有物報告主筆群 / 我熱愛關於軟體開發的任何事情,理想是開發出讓社會及全人類過得更好的軟體