Python 自 3.3 版開始提供虛擬環境 (PEP405, virtual environments in core, Sept. 29, 2012).

個人以為主要是為了清理出一個乾淨開發環境, 以便將來部署時不會多出一些有的沒的 package:

  • 別的專案用的, 而且這個專案用不到的.
  • 正在測試學習的.
  • 或者猜想著這個 package 到底是有沒有用到 (不曾記得安裝過, 但實際上是因為和另一個 package 相依而自動安裝進來了).
  • 避免 package 和 python 本身 (以及其他 package) 不相容的問題. 同時也可以避免這些多出來 package 引發不相容的問題. (小弟不才只寫過二個 python 小專案, 引用的東西不多, 所以一直沒有遇到)

新版本的 python 可能或多或少會和一些 package 不相容(註一), 尤其像是當年 python 3.0 那樣的大改版. 但是各個 package 之間的版本相依性也是經常會引發問題. 使用虛擬環境可以讓我們在構建專案時把環境先弄好, 並鎖定為專案的專用環境, 不必擔心因為別的專案的需求而弄壞了這個專案的環境.

比起另一個也是管理虛擬環境anaconda (since ver. 0.8.0, Jul. 13, 2012), python 自己所提供的虛擬環境管轄範圍小 (它只管 python 本身和 python packages), 所以使用上也相對的很簡便(註二):

  1. 先是針對專案進行開設虛擬環境.(註三)
  2. 設好之後, 所有和這個虛擬環境相關的工作 (安裝 packages, 測試/執行專案...等) 都要在虛擬環境啟動的狀態下進行. 所以, 新開啟的 bash terminal (或是 Windows Command Console) 使用前需要先啟動 (或者說是進入) 虛擬環境.
  3. 進入虛擬環境後, 所有的指令還是和沒有進入虛擬環境時完全一致, 不需要多加任何參數. 當然, 剛新開的虛擬環境裡面什麼 package 也沒有. 所以第一次啟動之後, 我們通常需要把要使用的 packages 遂一的安裝起來.
  4. 最後, 工作完成了, 就可以進行關閉 (或者說是離開) 虛擬環境.(註四)

註一: 其實, 有許多 package 之所以不相容很單純的只是因為製作 package 時把 python 版本鎖死了. 其中大部份是因為使用了 Python C API (此即 python3x.dll, 例如: python38.dll, python39.dll...).

早期 Python 內部資料結構或者功能但凡有一點小變動都會引起 C API 的變動. 所以使用 Python C API 的 package 在製作/打包 package 時也把 python 版本鎖死, 以避免因呼叫介面變動而造成系統不正常. 所以早期 Python 更新版本後 package 就必需重新為新版本的 Python 編譯及打包.

為此 Python 自 3.2 版起將已經非常穩定的部份獨立出來, 稱為 Limited API (此即 python3.dll). 使用 Python C API 的 Package 只要沒有用到 Limited API 之外的部份 (即還不是非常穏定的, 會再更動的部份) 就可以改用 Limited API, 以後 Python 升版也就不用大費周章重新編譯及打包.

註二: anaconda 管的多, 所以比較龐大 (anaconda 是動不動就幾個 GB 的怪獸, 真的很大), 所以只要是重覆的檔案都是使用連結 (link) 來建立複本. python 的虛擬環境也是盡可能的用 link, 但很可惜的是 windows 版預設是用 copy (可以加 --symlinks 參數啟用). 所以 windows 版中, 使用 python 的 venv 建立多少個虛擬環境(使用預設參數時), 就會有多少份 python.exe, pythonw.exe, pip.exe... 但因為 python 虛擬環境本身很小, 所以也算是可以接受 (呵呵! 其實加上一堆 package 之後也不小了, 尤其是 tensorflow 本身就至少有 1.1GB!!). 不過也請不要因為這樣就拼了命的開 venv 因為可能還有很多 package 要安裝啊.

python -m venv --symlinks envTest

使用 git bash 時則要先設一個環境變數 MSYS, 還需要 "以系統管理者身分執行" 上面那行指令才可以順利完成, 不然是會報錯的.

export MSYS=winsymlinks:nativestrict

註三: 除了個別專案用的虛擬環境之外, 我也會開 1~2 個學習/測試用的虛擬環境, 裡面就會什麼有用到的, 沒有到的, 雜七雜八的都裝.

註四: 其實是想關就關, 要用的時候再開即可.

所以總共三個新指令要認識一下:

  1. 開設
  2. 啟動
  3. 關閉

初始化: 開設虛擬環境


由於我們將會有各種不同的 虛擬環境, 為了方便區別, 我們需要為它命名. Python 會以此名稱建立一個目錄, 並將相關的資訊和檔案都放在該目錄中. 因此我們要先給虛擬環境取個簡單易懂的名字, 並決定它要放在哪裡. 然後執行下面的指令:

python -m venv /path/to/virtual_env_folder

Python 就會在你所指定的目錄中建立起虛擬環境 (目錄不存在 python 會自己幫你建立). 虛擬環境的管理 (命名及目錄位置) 可以依自己的需求, 我是直接在專案目錄裡, 進行開設虛擬環境. 例如:

# 直接開
$ python -m venv myProj\myEnv
# 或者分二段
$ cd myProj
$ python -m venv myEnv

這樣專案目錄 myProj 裡就有一個虛擬環境目錄 myEnv.

至於要不要把這個目錄放進 git 版本庫呢? 個人認為是不必, 因為有 pip freeze 指令可以產出需求的 packages 清單 (+版本需求), 只要使用
pip install -r 清單檔名(註五)
即可複製自己目前使用的 python 環境. 頂多是把 '清單檔名' 列入版本控制吧. 這樣子, 我們可以不必強制要求開發者用什麼名字作為虛擬環境的名字.

(myEnv) $ pip freeze > dependency.txt
(yourEnv) $ pip install -r dependency.txt

不過建議各位最好不要太依賴 pip freeze 來產生 requirements.txt. 最好是自己手動維護這個清單, 而且只要加入原始的套件就好 (例如: 加入 tensorflow, 而不是連同 tensorflow 要用到的一些 package 都加進去), pip 會為我們解決相依的 package 清單 (及版本號). 而且最好是不指定版本號, 除非必要才加上限制最小版本號 (用 >=, 最好不要用 == 指定單一版本號)(註六), 這樣日後的升版工作會容易許多.

如果 python 或者 package 需要進行升版, 也可以利用這個方法, 暫時另開一個虛擬環境, 進行升版及測試, 而不必動到原本的虛擬環境(註七, 註八).

註五: pip 指令並沒有限制 '清單檔名' 使用什麼檔名. 不過許多雲端環境都預設使用 requirements.txt 作為建立 python 執行環境的 python package '清單檔名'. 各位如果是要建一個自動部署到雲端環境用的 '清單檔名', 請記得把它改為 requirements.txt.

註六: 依據 Python 官方文件 PEP 440, 版本號限制的寫法可以用 , 將多組指定串起來. 版本號之前可以用: ~=, ==, !=, <, <=, >, >=, === 指定比對運算. 其中, ~= 是近似版本, 例如: ~=8.2.0 (相檔於 >=8.2.0 或者 ==8.2.*) 則所有 8.2.x 的版本都可以; ~=8.2 (相檔於 >=8.2 或者 ==8.*) 則所有 8.x.x 的版本都可以. 還有 === 是完全相等, 例如: ===1.0 則只有 1.0 版可以, 1.0+downstream1 是不行的.

註七: 注意: 虛擬環境開設後, 不要對虛擬環境目錄進行任何更名/搬動/複製 (任何影響到虛擬環境絕對路徑的活動都不行). 搬動它會使你的虛擬環境錯亂; 複製它, 複製出來的虛擬環境還是和原來的相關, 而不是獨立的一份. 這是因為, 目前 venv 在產生 activate 批次檔時是將虛擬環境的路徑 myEnv 直接寫死在檔案裡. activate 執行時就將該目錄下的Scripts 子目錄 (myEnv/Scripts, 而不是執行時自行判定目前所在位置) 附加在環境變數 PATH的最前面. 不過如果你自己有把握, 可以修正這些問題, 那就不在此限 (後果自負, 不要說我沒有提醒你; 另一個原因是還有許多 package 的 .pyc 檔都有記錄其全路徑.)

註八: 測完沒用的虛擬環境, 直接刪除即可. 萬一測試失敗, 可能會有 process 卡住而使得虛擬環境刪不乾淨, 則 window 關掉重開, 再刪一次就可以了. (Win7 有遇到過, Win10 用到現在還沒有發生過)

我的虛擬環境應該放在哪裡?(2021/05/19)


這個應該是個不是問題的問題. 怎麼說呢? 制式的答案是: 放哪裡都可以. 所以我看到有許多不熟悉 Windows (或者 Mac OS X, Linux 也是) 的初學者就習慣性的把 Python 虛擬環境開設在 '桌面' 上, 然後很有層次的分了許多層子目錄 (為了怕自己忘記了還用上了中文當目錄名稱). 這原本也沒什麼錯, 只是到後來需要在 Windows console 視窗 (cmd) 裡執行一些指令時, 就需要打比別人更長的指令 (甚至還要輸人中文), 然後又不懂得使用目錄開始的 1~2 個字元+Tab 鍵, 來加速目錄位置的輸入, 結果操作上也就比別人慢了許多.

個人比較推荐的目錄規劃是:

 +- vjupyter
 +- venvABC
 +- tools
 +- works
     +- proj1
         +- .git
         +- .vscode
         +- venv
     +- proj2
     +- proj3
  • 建立一個工作區, 一般會命名為 works. 在工作區裡, 依自己的需求開專案目錄.
  • 建立一個工具子目錄 (tools), 並將它加入環境變數 PATH 裡. 裡面用來放一些零散的工作程式和批次檔. 例如: windows 版的 wget 指令, nuget 指令, ffmpeg 指令...
  • 專案目錄和虛擬環境的命名原則: 依自己喜好, 但盡量簡短, 不要有中文.
  • 虛擬環境分為共用專用二類.
    • 共用虛擬環境放在工作區 (works) 同一層. 裡面裝的 package 會比較雜, 使用時缺什麼就裝什麼.
      例如: 開一個虛擬環境名為 vjupyter, 裡面安裝 jupyterlab, 及使用 jupyter 環境所需的所有 package, 用於學習及測試.
    • 專用虛擬環境放在工作區 (works) 下的專案目錄裡. 裡面則只裝的專案有用到的 packages.
  • 盡量將這些虛擬環境, 專案放在第二個磁碟機裡. (如果有第二個磁碟機的話, 主要是為了方便備份及其他操作, 還有萬一系統故障了, 還可把它們救回來.)
  • 可以自己建立一些批次檔用來啟動虛擬環境及一些必要的設定. 例如: 下面是我用來啟動 jupyter 環境的批次檔 lab.bat. 各位可以依自己的需求修改第12行. 並把它放在工具目錄 tools 裡.
    • %CD%\%1 改成自己的 jupyter 虛擬環境目錄.
    • 如果你不會 jupyter 的設定可以暫時把後面的 --config=.\lab_cfg.py --notebook-dir=%CD%\NBs 刪掉即可.
    • 如果你會 jupyter 的設定, 可以自行產生設定檔, 並把 .\lab_cfg.py更改為自己的設定檔.
    • 還有, 把 %CD%\NBs 改成自己放 .pynb 檔的子目錄.
    @echo off
    if [%1] == [] (
        echo Usage: %0 venv_name
        goto exit
    )
    
    if not exist %CD%\%1\Scripts\activate.bat (
        echo %1 is not a python venv folder
        goto exit
    ) else (
        set JUPYTER_DATA_DIR=%CD%
        %CD%\%1\Scripts\activate & jupyter lab --config=.\lab_cfg.py --notebook-dir=%CD%\NBs
        deactivate
        set JUPYTER_DATA_DIR=
    )
    :exit
    

另外, 我發現一些初學者對於專案目錄和虛擬環境的界定不清楚, 常常會把自己的程式 (.py 檔或者 .ipynb 檔) 放進虛擬環境的目錄裡. 請千萬不要這麼做. 請你務必記得不要把自己的程式放進虛擬環境的目錄裡.

虛擬環境目錄結構


Windows 環境下所開出來的虛擬環境目錄結構如下圖:

python venv.png

Windows 環境的虛擬環境目錄結構

其中比較重要的是檔案 pyvenv.cfg 以及 LibScripts 這二個目錄.

  • pyvenv.cfg 是這個虛擬環境的設定檔.
  • Scripts 裡放的是相關的執行檔dll, 如: python.exe, activate.bat.
  • 而我們之後使用 pip install 安裝的 packages 都會進到 Lib/site-packages 這個目錄.
  • 另外, packages 若含有相關的 執行檔dll 則也會加到 Scripts 目錄中.

OSX 與 Linux 環境二者和 Windows 環境主要的差異是 Scripts 目錄改為 bin, 還有 activate.bat 變成了沒有副檔名的 bash script activate.

啟動虛擬環境


請注意: 你下指令時所處的目前路徑. 這裡所列出的指令例, 所用的都是相對路徑, 所以虛擬環境都是在目前路徑的目錄裡.

OSX 和 Linux 環境下, 請使用以下指令:

source myEnv/bin/activate

或者 Windows 命令提示字元指令

myEnv\Scripts\activate.bat

或者在 Git for Windows 的 Git Bash 裡 (是的用它也行)

source myEnv/Scripts/activate

請把其中的 myEnv 要換成你自己設定的虛擬環境目錄名稱. 啟動虛擬環境之後, 系統提示符號之前會多一個 (myEnv), 用以提醒我們是在哪一個虛擬環境中.

OSX 和 Linux 環境的提示如下:

(myEnv) $

或者 Windows 系統提示:

(myEnv) D:\>

其實說穿了, 啟動虛擬環境並沒有幹什麼大事, 只是

  • 虛擬環境下的 Scripts 路徑加到環境變數 PATH 的最前面, 讓系統優先找到放在它底下的 python 執行檔.
  • 改一下系統提示, 把虛擬環境的目錄名加在原本的系統提示前面.

如此而已. 這也是為什麼虛擬環境可以想關就關, 要用再開. 還有新開啟的 bash terminal (或是 Windows Command Console) 使用前需要先啟動 (或者說是進入) 虛擬環境.

關閉虛擬環境


OSX, Linux 和 Windows 環境都是使用以下指令:

deactivate

下達指令之後, 環境變數 PATH 和系統提示會回復到原本的樣子.

因為並沒有什麼程式在背後執行, 所以如果把啟動虛擬環境的 bash 終端機 (或 Windows command console) 直接關掉並不會引發任何問題.

其他相關指令


升級虛擬環境內的 python


虛擬環境的 python 升級了 (或者重新安裝, 換路徑了也行), 如果也想要將虛擬環境的 python 也升級上來, 怎麼辦呢?

python -m venv --upgrade myEnv

事實上它也可以用來降版. 只不過我們需要明確指定用哪個路徑下的 python.exe. 不過, 在 Windows 平台裡, 我們有 py.exe 可以用, 所以可以簡單一點:

py -3.6 -m venv --upgrade myEnv

只要你有安裝 Python v3.6.x, 這樣就可以降到 3.6 版.

另外, 由於啟動虛擬環境後, 虛擬環境的 Scripts (或者 bin) 路徑會自動附加在環境變數 PATH 的最前面, 所以 Python 安裝時並不需要修改環境變數 PATH. (我們只有在新設定虛擬環境時需要它, 平時的工作完全不需要, 更何況 Windows 平台還有一個 Python Launcher: py.exe 可以用來啟動不同版本的 python.)

查看 python 系統環境


我們可以查看一下 python 系統環境在沒有啟動虛擬環境, 和啟動了之後 有何不同. 下面列的是啟動虛擬環境後的:

(vLAB2) D:\Works\jupyter> python -m site
sys.path = [
    'D:\\Works\\jupyter',
    'D:\\Python38\\python38.zip',
    'D:\\Python38\\DLLs',
    'D:\\Python38\\lib',
    'D:\\Python38',
    'D:\\Works\\jupyter\\vLAB2',
    'D:\\Works\\jupyter\\vLAB2\\lib\\site-packages',
    'D:\\Works\\jupyter\\vLAB2\\lib\\site-packages\\win32',
    'D:\\Works\\jupyter\\vLAB2\\lib\\site-packages\\win32\\lib',
    'D:\\Works\\jupyter\\vLAB2\\lib\\site-packages\\Pythonwin',
]
USER_BASE: 'C:\\Users\\User001\\AppData\\Roaming\\Python' (exists)
USER_SITE: 'C:\\Users\\User001\\AppData\\Roaming\\Python\\Python38\\site-packages' (exists)
ENABLE_USER_SITE: False

應該至少會有 4 組不同. 最重要的, 使用 python 執行檔的來源不同, 還有 ENABLE_USER_SITE 變成 False 了.

VS code 與 虛擬環境


MS VS Code 認得 Python 的虛擬環境: MS VS Code 可以和anaconda的虛擬環境配合, 也可以和 Python 自己的虛擬環境配合.

請注意: VS Code 只認得專案目錄以下的 venv 虛擬環境, 所以務必設定並開啟一個專案目錄並將 venv 虛擬環境開設在專案目錄下 (在專案目錄以下即可, 不限定一定要開在第一層子目錄).目前 VS Code (v1.55.2) 已經修復這個 bug, 你可以依照自己的需求或喜好將 python 的虛擬環境開設在任何目錄中, 只要正確設定 python interpreter 的路徑即可(2021/05/14).

如果虛擬環境還沒有開設, 建議你直接在 VS Code 的終端機環境中直接開設. VS Code 會問你是不是要直接將 python interpreter 設定成使用這個新開的虛擬環境, 可以省去自己設定的麻煩. 如果虛擬環境已經開設了, 則請使用下述的方法.

例如我已經開設好一個虛擬環境 vLAB2, 在 windows command console 中, 啟動虛擬環境 vLAB2 之後, 輸入:

(vLAB2) D:\Works\jupyter\> code .

進入 VS Code 之後, 按 Ctrl+Shift+P (或者 F1), 輸入 python: select interpreter, 然後選擇位於 vLAB2/Scripts 子目錄中的 python.exe.

設好之後, 你應該可以看到左下方的 python 指示器應該改為有帶虛擬環境名字的指示 (vLAB2). 這時在 VS Code 執行 (或者除錯) python 程式時, 都會在虛擬環境中進行.

/tmp/php4MRfsj

VS Code 中使用 python venv. 虛擬環境名稱為 vLAB2
這一張截圖是 VS Code preview 的所以是紫色的, 正常版的藍色的.

你也可以在不啟動虛擬環境的狀況下進入 VS Code. 二者的差異只是第一個終端機 (terminal session) 有沒有自動進入虛擬環境 (Terminal session 可以使用快速鍵 Ctrl+` 打開或關閉). 要注意的是第二個 (和之後開啟的) terminal session 是以你選定的 python interpreter 為主, 如果你選的是以虛擬環境下的 python.exe 作為預設的 python interpreter, 則新打開的 terminal session 都會自動進入虛擬環境 (選定的 python interpreter 是一般安裝的 python.exe 則不會進入虛擬環境)(2021/05/14).

如果想在虛擬環境中安裝工作 python package, 我們不必一定要另外開啟一個 Windows command console 來工作. 直接在 VS Code 裡開一個新的 terminal 視窗就可以看到進入虛擬環境的系統提示符號 (或者由原本的 terminal 輸入啟動虛擬環境的指令也可以).

更新 2023/10/26


最近發現虛擬環境其實是可以搬動的. 搬到不同路徑, 搬到別的磁碟機, 甚至是搬到別的機器 (CPU/作業系統相同) 都沒問題.

只要在搬動後, 檢查<虛擬環境目錄>下的二組檔案, 與搬動後的環境不同時修正即可:

  1. pyvenv.cfg

    檔案內容大約是如下所示:

    home = C:\Program Files\Python310
    include-system-site-packages = false
    version = 3.10.8
    

    要注意的是home所記錄的和現下用的 Python 是否路徑相同. 不同機器時請注意用同一個版本的 python (大版號及次版號相同即可, 最小的版號可以不同. 小版號不同時可以順便修改一下 version 所記錄的)

    請注意: home所記錄的 Python 是否路徑不存在, 或者不對會影響到 vscode 無法設定 python 虛擬環境.

  2. 修改啟動檔 activate 內所記錄的<虛擬環境目錄>. 啟動檔一共有三個, 請依自己的需求修改需要用到的.
    • activate: 用於 git-bash 環境.
    • activate.bat: 用於 cmd 命令提示字元環境.
    • activate.ps1: 用於 powershell 環境. 註: 這個檔不必修改.

    要修改的是定義環境變數VIRTUAL_ENV的地方. activate 大約在第 40 行

    VIRTUAL_ENV="D:\xWork\You-Get\vYou"
    export VIRTUAL_ENV
    

    activate.bat大約在第 11 行

    set VIRTUAL_ENV=D:\xWork\You-Get\vYou
    

一般這樣就可以成功移動虛擬環境的位置. 不過虛擬環境的位置是否可以搬動, 有時還有其他因素的影響: 主要是專案裡是否有用到虛擬環境路徑? 該部份程式碼是否一定要原先的路徑.

其他


其他的指令和沒在進入虛擬環境之前的都一樣.

相關文章連結


更新 2020/11/11


才剛說嘴就打嘴, 昨天安裝 python 3.9 + jupyterlab + jupyterlab-git + matplotlib 失敗了. 好在原本的環境完好如初. 同時幾經奮鬥也終於完成新環境的設定, 改天再把解法整理出來. (請參考這一篇貼文)

arrow
arrow

    MagicJackTing 發表在 痞客邦 留言(1) 人氣()