MOOSSin
選單

全部文章 · 開源工具

在 Windows 上用 Python:直譯器從哪來、venv 怎麼釘、UTF-8 怎麼開

Windows 上的 Python 有幾個別的平台沒有的坑:PATH 上的 python 可能是商店的空殼、embeddable 套件會忽略 PYTHONPATH、主控台編碼不是 UTF-8。這篇把我現在的做法整理出來,附上鎖定檔和雜湊安裝的習慣,還有一段關於「哪一個 python」的靈異故事。

先說一個真的發生過的靈異故事。

某天晚上我在 Windows 的終端機裡打了 python,按下 Enter。什麼都沒有發生。沒有錯誤訊息,沒有提示字元,游標閃了一下,換行,結束。我以為是終端機壞了,開了另一個,一樣。我以為是鍵盤壞了,打了 dir,正常。只有 python 這個字像對著空氣講話。

那天我大概浪費了一個下午。答案是:Windows 10 和 11 在 PATH 上放了 python.exe 和 python3.exe 兩個「應用程式執行別名」,指向 Microsoft Store。沒有安裝 Python 的機器上,執行它會開商店;某些設定下它會安靜地結束,沒有錯誤、沒有輸出。它不是 Python,它是一個長得像 Python 的門牌,門後面什麼都沒有。

從那天起我有一條鐵律:自動化腳本絕對不能依賴 PATH 上的 python。用 py -3 啟動器,或者更好,用專案自己那一份直譯器的完整路徑。

為什麼在 Windows 上養 Python 要特別小心

CPython 是 Python 的參考實作,PSF 授權,目前的穩定版是 3.13。它在 Windows 上很好裝,官網下載、下一步下一步、完成。問題不在「裝得起來」,在「每次跑的都是同一個」。一台用了幾年的 Windows 電腦上,可能同時有商店版、官網版、某個編輯器自帶的版、某個工具偷偷裝的版。你的程式今天用 A 跑得好好的,明天不知道為什麼用了 B,然後某個套件不見了。

這就像你有五支長得一樣的原子筆散在桌上,其中兩支沒水。你每次隨手抓一支,能不能寫字全看運氣。解法不是把筆都丟掉,是替每一份作業指定一支筆,用完放回原位。

直譯器跟著專案走

我的做法是每個專案目錄裡放一份固定版本的 CPython,再用它建 venv,專案的所有指令都透過 venv 裡的 python.exe 執行。啟動腳本檢查 venv 的基底是不是那一份,不是就直接失敗,不退回系統上碰巧有的版本。

「直接失敗」聽起來很不友善,但這是我最喜歡的一種不友善。它在你還沒開始跑之前就告訴你「你拿錯筆了」,而不是讓你寫完三頁才發現字都是斷斷續續的。

git worktree 另開工作目錄時,venv 要整份複製過去並改 pyvenv.cfg 裡的基底路徑。多一步,有點煩,但測試永遠跑在對的直譯器上。

embeddable 套件的 ._pth

官方有一個「embeddable」的 zip 版本,解壓就能用,不用安裝程式,很適合打包工具或放進專案裡。它內含一個 python313._pth 檔,這個檔案會取代一般的路徑計算:工作目錄不會加進 sys.path,PYTHONPATH 環境變數也被忽略。

翻成人話:原樣解壓的直譯器連你自己的模組都匯入不了。你會看到 ModuleNotFoundError,然後檢查檔案在不在,在;檢查名字對不對,對;檢查有沒有 __init__.py,有。一切都對,就是找不到。在那個 ._pth 檔裡加一行 . 或你的原始碼路徑就好,但你得先知道它的存在。這種「文件上有寫,但沒有人會在第一次就去讀」的東西,Windows 上的 Python 特別多。

兩份鎖定檔,安裝時要求雜湊

同一份 requirements.txt 在 Windows 和 Linux 解出來的版本不一定相同,有原生擴充的套件尤其如此。我留兩份鎖定檔,各自在對應的平台上產生,每一行帶 SHA-256。安裝時用 pip install --require-hashes --only-binary=:all:,前者要求每個 wheel 的雜湊對得上,後者禁止現場從原始碼編譯。

「禁止現場編譯」這件事在 Windows 上特別有價值。你一定看過那種安裝到一半開始噴 C 編譯器錯誤的畫面,紅字捲了三頁,最後一行說「需要 Visual Studio Build Tools」。加了 --only-binary 之後,那種情況會變成一個乾淨的「找不到對應的 wheel」,你至少知道問題在哪,而不是被拖進一個你根本不想進去的世界。

同一份原始碼在兩個平台上建出逐位元組相同的產物,是可以做到的,我驗證過。第一次看到兩邊的雜湊完全一樣的時候,有一種莫名的感動,像是兩個從來沒見過面的人寫出了一模一樣的字。

主控台不是 UTF-8

繁體中文的 Windows 主控台預設是 cp950,Python 的標準輸出跟著它走。程式印出一個表情符號或某個罕見字,就會在編碼那一行炸掉,而且錯誤訊息長得像程式本身有 bug,你會去 debug 程式邏輯,debug 半天發現邏輯完全正確,只是它想印一個它印不出來的字。

跑測試和腳本時帶 PYTHONUTF8=1 開啟 UTF-8 模式,或者在程式裡明寫編碼。PEP 686 已經決定 3.15 起 UTF-8 模式是預設,在那之前這個環境變數會一直留在我的每一支腳本裡,像一張永遠貼著的便條紙。

小劇場:我到底裝了幾個 Python

「我的電腦裡好像有三個 Python,要刪掉哪個?」

「先不要刪,你不知道哪個程式在用哪一個。」

「那我怎麼知道我現在用的是哪個?」

「python -c "import sys; print(sys.executable)",它會告訴你自己住在哪裡。」

「它印出一個我從來沒看過的路徑。」

「恭喜,你找到第四個了。」

「這樣正常嗎?」

「非常正常。這就是為什麼專案要自己帶一份,不要靠系統上的。」

少裝套件

能用標準函式庫就用標準函式庫。sqlite3、html.parser、urllib、hashlib、unittest 涵蓋的範圍比多數人以為的大。少一個套件,就少一個要在兩個平台上對版本、對雜湊、對授權的東西。

我知道這聽起來很像長輩在說「能走路就不要搭車」。但在 Windows 上養 Python 這件事,真的是能少一個變數就少一個變數。每一個你沒裝的套件,都是一個不會在半夜壞掉的東西。

幾個關於 Python 的小知識

Python 的名字跟蛇沒有關係,來自英國喜劇團體 Monty Python。Guido van Rossum 在 1989 年聖誕節假期開始寫它,因為那時候辦公室關門,他很無聊。官方文件裡到處都是 spam 和 eggs 這種例子,就是這個喜劇團體的梗。知道這件事之後,你看那些範例程式碼會多一點親切感。

Python 的每一個重大變更都要經過 PEP,Python Enhancement Proposal,一份公開的提案文件,大家在上面吵,吵完決定。前面提到的 PEP 686 就是這樣的一份,決定 UTF-8 模式什麼時候變預設。你可以去讀那些 PEP,裡面有很多「為什麼要這樣設計」的討論,比任何教學都能讓你理解這個語言在想什麼。

3.13 版有一個實驗性的「自由執行緒」版本,執行檔叫 python3.13t,拿掉了那個困擾大家二十年的全域直譯器鎖。它還是實驗性的,有些套件不相容,但這代表 Python 終於認真在處理多執行緒效能這件事。我還沒在正式的東西上用它,但我很期待哪天可以。

常見的三個誤會

第一個誤會是「Windows 上的 Python 比較差」。核心是一樣的,差的是周邊:PATH、編碼、路徑分隔符號、沒有內建的編譯器。這些都有解法,只是要知道。

第二個誤會是「venv 是為了隔離套件」。是,但更重要的是隔離直譯器。一個 venv 記著它是從哪一份 Python 建出來的,那份 Python 不見了或換了,venv 就壞了。這是為什麼我堅持直譯器跟著專案走。

第三個誤會是「pip install 裝的東西是安全的」。PyPI 上任何人都能上傳套件,名字打錯一個字母可能就裝到別人故意放的東西。雜湊鎖定檔和 --require-hashes 不是偏執,是對「任何人都能上傳」這個事實的合理回應。

一個週末可以試的小練習

在 Windows 上開一個終端機,打 where python,看它列出幾個。打 py -0,看啟動器認得幾個版本。然後選一個資料夾,用 py -3.13 -m venv .venv 建一個 venv,打開 .venv\pyvenv.cfg 看裡面的 home 那一行,那就是這個 venv 的基底住哪裡。

接著故意做壞事:把那個基底的 Python 資料夾改名,再執行 .venv\Scripts\python.exe。看它怎麼死。改回來,再執行,活了。這五分鐘會讓你永遠記得 venv 和直譯器之間那條看不見的線。

最後開一個新終端機,set PYTHONUTF8=1,印一個表情符號。再開一個不設的,印同一個。兩個視窗並排看,你會理解為什麼那個環境變數會一直留在我的腳本裡。

後記

那個「打 python 什麼都不會發生」的下午,我後來把它當成一個入門儀式。每個在 Windows 上認真用 Python 的人大概都經歷過一次,然後從此對 PATH 上的東西保持警覺。如果你還沒經歷過,希望這篇文章讓你可以直接跳過那個下午,把時間拿去做別的事,比如泡一杯茶。

官方網站:python.org

閱讀偏好

返回全部文章作者與內容更正

留言(0)

還沒有留言。讀完有想法,歡迎留下第一則。

留言經站長審核後才會顯示。暱稱和留言會公開,請不要寫下電話、地址等個人資料;資料怎麼處理,請見隱私權政策。