by Koma Zhangby Koma Zhang

2024/04/25:新增「更新 Python 版本」。

「人生苦短,我用 Python」闡述了 Python 的易用與簡潔,但如果深入到 Python 的套件管理與版本隔離,則是一件十分複雜的事,有興趣的人可以參考下列內容:

本文重點

這次並沒有要細論上述內容,只是想把 Anaconda 移除掉——我現在的 Python 虛擬環境(virtualenv)都是用 Anaconda 附帶的 conda 建立與管理居多。

而最常見的替代方案,就是 pyenv + virtualenv。

本篇僅記錄一下設定上的重點,詳細用法請參考 pyenv 官方 GitHub

為何要從 conda 轉向 pyenv?

主要是為了「簡潔」。另外,使用 pyenv 可以更方便地管理不同版本的 Python,切換上的靈活性不是 conda 所能比的。

所有用 conda 建立的虛擬環境都同時可以用 pip 或 conda 來安裝第三方套件,這樣容許混雜的模式讓我略感煩躁。

其次是 conda 預設的 base env 真的很肥大,畢竟幫你灌好了一堆資料科學套件。雖然方便,但也往往一併安裝了許多你根本用不上的東西,可謂雙面刃

不同層級的虛擬環境管理:Python vs 專案

值得一提的是,「不同 Python 版本間的虛擬環境管理」「不同專案間的虛擬環境管理」基本上是兩件事,只不過 Anaconda 透過 conda 同時都能做到而已。conda 在建立虛擬環境的同時會要求你選擇 Python 版本,所以不同 conda env 間的 Python 版本可以不同。

而 pyenv 則是比較偏向 Python 版本管理,雖然你要直接當套件管理的虛擬環境來用也未嘗不可,但還是推薦使用 virtualenv 較為合理。

Python 層級的虛擬環境管理方案

不在本機的全域直接安裝 Python,而是透過不同的隔離手段,安裝並管理複數版本的 Python,方便不同專案與需求使用。

  • conda:即將捨棄。
  • pyenv:本文重點。
  • Docker:以容器建立開發環境也是一個趨勢,比如 VS Code 的 DevContainer,這部分在此先不討論。

jetbrains.com/lp/python-developers-survey-2020jetbrains.com/lp/python-developers-survey-2020

單一專案虛擬環境管理方案

一般定義上的 Python 虛擬環境,著重於專案所安裝的套件與套件版本

  • Python 內建的 venv:通常直接放在特定專案的根目錄底下,因為沒有更高層的封裝讓你可以輕鬆跨專案打開,路徑要自己找,較適合單一專案使用。
  • virtaulenv:最常見的虛擬環境管理方案。
  • pyenv-virtualenv:pyenv 的 virtualenv 高階封裝版本。
  • Pipenv、Poetry 等套件管理工具:前者小荒廢,後者還很新,主要的亮點都是在處理套件之間的版本依賴問題,現階段暫時不考慮。

以上都只是套件管理,如果要連同 Python 的版本一起管理(這顯然是個硬需求),還是需要 pyenv。


安裝、設定 pyenv

1
2
brew update
brew install pyenv

裝完之後要設定.bashrc.zshrc,還有.bash_profile.zprofile,端視你的 shell 是哪個。目前新版本的 macOS 預設 shell 為 zsh

相關文章:Ubuntu 安裝使用 zsh + 輕量級套件管理器 zinit


2023/04/07重要更新:pyenv 2.3.0 以後,已經大幅簡化了設定操作,本段內容在新版仍可適用,但建議直接升級最新版 pyenv 並直接改用以下設定

1
2
3
export PYENV_ROOT="$HOME/.pyenv"
command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"

官方的 shell 指令版本(for zsh):

1
2
3
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.zshrc
echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.zshrc
echo 'eval "$(pyenv init -)"' >> ~/.zshrc

這三行設定,無論 bash 或 zsh 皆同;.profile.zprofile 如果需要,增加的內容也完全一樣!真的簡化了很多,不用再去判斷是哪種 shell 了。


zsh 使用者

設定的內容主要是提供正確的 Python PATH,這部分很關鍵。具體要新增什麼內容,各教學或官方的版本沒有完全一致,不過大同小異,在此提供我實際新增的部分,注意以下皆是以 Homebrew 安裝的 pyenv 為例:

  • .zprofile新增

你可能跟我一樣一開始也沒有這個檔,直接用官方給的指令新增就好:

1
echo 'eval "$(pyenv init --path)"' >> ~/.zprofile
  • .zshrc新增
1
2
3
if command -v pyenv 1>/dev/null 2>&1; then
eval "$(pyenv init -)"
fi

特別說明,官方教學只說需要新增中間的eval "$(pyenv init -)",但我光這樣設定,會無法切換全域 Python 版本,看了這篇之後改成上述的版本。

設定好後記得要分別 source 一下。

bash 使用者

如果是 bash 用戶,建議設定上一律直接使用官方提供的這兩段 script 就好了,最簡單無腦且不會出錯,記得兩個部分都要執行喔!

第一部分

1
2
3
4
5
6
7
8
9
10
11
echo -e 'if shopt -q login_shell; then' \
'\n export PYENV_ROOT="$HOME/.pyenv"' \
'\n export PATH="$PYENV_ROOT/bin:$PATH"' \
'\n eval "$(pyenv init --path)"' \
'\nfi' >> ~/.bashrc
# 注意檔名
echo -e 'if [ -z "$BASH_VERSION" ]; then'\
'\n export PYENV_ROOT="$HOME/.pyenv"'\
'\n export PATH="$PYENV_ROOT/bin:$PATH"'\
'\n eval "$(pyenv init --path)"'\
'\nfi' >>~/.profile

不過要稍為注意一下.profile名稱,在 macOS 上通常為.bash_profile

1
2
3
4
5
echo -e 'if [ -z "$BASH_VERSION" ]; then'\
'\n export PYENV_ROOT="$HOME/.pyenv"'\
'\n export PATH="$PYENV_ROOT/bin:$PATH"'\
'\n eval "$(pyenv init --path)"'\
'\nfi' >>~/.bash_profile

第二部分

1
echo 'if command -v pyenv >/dev/null; then eval "$(pyenv init -)"; fi' >> ~/.bashrc

上述設定完後記得要source或重啟 shell。

安裝 pyenv-virtualenv

請直接參考另一篇文章的內容


簡單使用案例

以我的例子:

  1. 建立一個全域的 Python 3.7.11 環境。
  2. 非必要:將當前 shell 的 Python 版本切換到 3.8.10
  3. 建立一個使用 Python 3.7.11 的虛擬環境。
1
2
3
4
5
6
7
8
9
10
#1
pyenv install 3.7.11
pyenv global 3.7.11 # 將全域的 Python 版本設為 3.7.11

#2 非必要
pyenv install 3.8.10
pyenv shell 3.8.10 # 將目前 shell 的 Python 版本設為 3.8.10

#3
pyenv virtualenv 3.7.11 my_venv<自行命名>

如果已經在 pyenv 的 3.7.11 環境下,則上述 #2 的3.7.11可以省略,只要pyenv virtualenv my_venv即可建立虛擬環境。

啟用與停止虛擬環境

1
2
pyenv activate my_venv
pyenv deactivate my_venv

移除虛擬環境

1
pyenv uninstall my_venv

查看目前所有虛擬環境

1
pyenv virtualenvs

你會看到每一個虛擬環境都有兩個項目,這是正常的:

There are two entries for each virtualenv, and the shorter one is just a symlink.

像下面這樣:

1
2
3.7.11/envs/my_venv (created from /Users/kyo/.pyenv/versions/3.7.11)
* my_venv (created from /Users/kyo/.pyenv/versions/3.7.11)

更新 Python 版本

使用指令查看當前所有可安裝的 Python 版本:

1
pyenv install --list

安裝 pyenv 後,用了一段時間,可用的 Python 版本一定會有更新,但 pyenv 並不會自動更新這些 Python 版本。

可以使用以下指令更新 pyenv(使用 homebrew 安裝):

1
brew upgrade pyenv

或參考其它更新方式

1
pyenv update

1
2
cd $(pyenv root)
git pull

結論

使用 pyenv 建立不同 Python 版本並管理相對應的虛擬環境實在非常方便!

附帶一提,雖然不一定要安裝 pyenv-virtualenv 而可以直接pip virtualenv即可,但後者的使用方式類似內建的 venv,比較沒有那麼彈性。

此外,pyenv-virtualenv 在 3.3 版本以上的 Python,實際上就是調用內建的 venv 來建立虛擬環境,否則就是調用 virtualenv,總之就是多一層封裝讓使用變得更方便而已。

參考