在Windows系統(tǒng)下管理多版本Python的工具實現(xiàn)方案
手把手打造 Python 多版本管理工具,告別 PATH 混亂,輕松切換
寫在前面
作為一名開發(fā)者,我的電腦上常年安裝著多個 Python 版本:2.7、3.7、3.8、3.9、3.10……每次需要切換時,要么手動修改系統(tǒng)環(huán)境變量 PATH,要么在項目里硬編碼路徑,煩不勝煩。早就羨慕 Linux 下的 nvm(Node Version Manager)和 pyenv,于是決定在 Windows 上也造一個類似的輪子。
本文記錄了我從編寫 PowerShell 腳本、封裝成全局命令,到解決執(zhí)行策略限制的完整過程。最終的效果是:在 PowerShell 中直接輸入 pvm list、pvm use 3.9.10,就能像使用內(nèi)置命令一樣管理 Python 版本。
一、方案選擇:PowerShell 函數(shù) + 全局調(diào)用
實現(xiàn)一個“全局命令”通常有三種方式:
將腳本所在目錄添加到 PATH 環(huán)境變量
最正統(tǒng)的做法,任何命令行都能識別。但需要處理管理員權(quán)限(修改系統(tǒng) PATH)以及腳本執(zhí)行策略。
在 PowerShell Profile 中定義函數(shù)
僅在 PowerShell 中有效,但無需修改 PATH,權(quán)限要求低。適合純 PowerShell 用戶。
編寫批處理包裝器 + 添加 PATH
兼容 CMD 和 PowerShell,但同樣涉及 PATH 修改和提權(quán)。
考慮到我主要使用 PowerShell,且希望操作盡量簡單,我選擇了 方案二:在 PowerShell Profile 中定義一個函數(shù) pvm,該函數(shù)調(diào)用我的核心腳本 pvm.ps1。這樣只需要把腳本放在固定位置,然后在 profile 中寫一行函數(shù)定義,即可在任何 PowerShell 窗口中使用 pvm 命令。
二、核心腳本:pvm.ps1
首先編寫核心邏輯的 PowerShell 腳本。腳本的功能包括:
- 列出所有已配置的 Python 版本
- 顯示當(dāng)前正在使用的版本
- 切換到指定版本(修改系統(tǒng)
PATH,將目標(biāo)版本的路徑提到最前面) - 交互式菜單
完整代碼如下,你只需修改 $pythonVersions 哈希表中的路徑為你自己的實際安裝目錄即可。
# pvm.ps1 - Python Version Manager for Windows
param(
[string]$Command,
[string]$Argument
)
# 要求以管理員身份運行(因為要修改系統(tǒng) PATH)
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Error "此腳本需要以管理員身份運行。請右鍵點擊 PowerShell 并選擇「以管理員身份運行」,然后重試。"
exit 1
}
# ---------- 配置區(qū)域:請根據(jù)您的實際安裝路徑修改 ----------
$pythonVersions = @{
"2.7.3" = @{
Path = "D:\Develop\python-2.7.3"
Scripts = "D:\Develop\python-2.7.3\Scripts"
}
"3.7.4" = @{
Path = "D:\Develop\Python37"
Scripts = "D:\Develop\Python37\Scripts"
}
"3.7.9" = @{
Path = "D:\Develop\Python-3.7.9"
Scripts = "D:\Develop\Python-3.7.9\Scripts"
}
"3.8.10" = @{
Path = "D:\Develop\python-3.8.10"
Scripts = "D:\Develop\python-3.8.10\Scripts"
}
"3.9.10" = @{
Path = "D:\Develop\python-3.9"
Scripts = "D:\Develop\python-3.9\Scripts"
}
"3.10.8" = @{
Path = "D:\Develop\python-3.10.8"
Scripts = "D:\Develop\python-3.10.8\Scripts"
}
}
# ---------------------------------------------------------
# 收集所有已知的 Python 相關(guān)路徑(用于后續(xù)過濾)
$allPythonPaths = @()
foreach ($ver in $pythonVersions.Keys) {
$allPythonPaths += $pythonVersions[$ver].Path
$allPythonPaths += $pythonVersions[$ver].Scripts
}
$allPythonPaths = $allPythonPaths | Where-Object { $_ -ne '' } | ForEach-Object { [System.IO.Path]::GetFullPath($_) }
# 獲取系統(tǒng) PATH(機(jī)器級別)
function Get-SystemPath {
return [Environment]::GetEnvironmentVariable("Path", "Machine") -split ';' | Where-Object { $_ -ne '' }
}
# 設(shè)置系統(tǒng) PATH
function Set-SystemPath($paths) {
$newPath = $paths -join ';'
[Environment]::SetEnvironmentVariable("Path", $newPath, "Machine")
$env:Path = $newPath # 立即更新當(dāng)前會話
}
# 獲取當(dāng)前正在使用的 Python 版本(如果屬于管理列表則返回版本號,否則返回 $null)
function Get-CurrentPythonVersion {
try {
$pythonExe = (Get-Command python -ErrorAction Stop).Source
} catch {
Write-Warning "當(dāng)前 PATH 中未找到 python 命令。"
return $null
}
$pythonExe = [System.IO.Path]::GetFullPath($pythonExe)
foreach ($version in $pythonVersions.Keys) {
$info = $pythonVersions[$version]
$pythonDir = [System.IO.Path]::GetFullPath($info.Path)
$expectedExe = Join-Path $pythonDir "python.exe"
if ($pythonExe -eq $expectedExe) {
return $version
}
}
return $null
}
# 獲取當(dāng)前 Python 的完整路徑(用于顯示)
function Get-CurrentPythonPath {
try {
return (Get-Command python -ErrorAction Stop).Source
} catch {
return $null
}
}
# 列出所有版本,并標(biāo)記當(dāng)前版本
function List-Versions {
$current = Get-CurrentPythonVersion
Write-Host "已安裝的 Python 版本:"
foreach ($version in ($pythonVersions.Keys | Sort-Object)) {
if ($version -eq $current) {
Write-Host "* $version (當(dāng)前)" -ForegroundColor Green
} else {
Write-Host " $version"
}
}
}
# 切換到指定版本
function Use-Version($version) {
if (-not $pythonVersions.ContainsKey($version)) {
Write-Host "錯誤:版本 '$version' 不存在。" -ForegroundColor Red
return $false
}
$current = Get-CurrentPythonVersion
if ($current -eq $version) {
Write-Host "當(dāng)前已經(jīng)是 Python $version,無需切換。" -ForegroundColor Yellow
return $true
}
# 新版本需要添加的路徑
$newPaths = @($pythonVersions[$version].Path, $pythonVersions[$version].Scripts) | Where-Object { $_ -ne '' }
$newPaths = $newPaths | ForEach-Object { [System.IO.Path]::GetFullPath($_) }
# 獲取當(dāng)前系統(tǒng) PATH
$currentPath = Get-SystemPath
# 過濾掉所有已知的 Python 路徑
$filteredPath = $currentPath | Where-Object {
$item = $_
try {
$fullItem = [System.IO.Path]::GetFullPath($item)
} catch {
$fullItem = $item
}
$matched = $false
foreach ($known in $allPythonPaths) {
if ($fullItem -eq $known) {
$matched = $true
break
}
}
-not $matched
}
# 構(gòu)建新 PATH:新路徑在前,其余在后
$newPathList = $newPaths + $filteredPath
Set-SystemPath $newPathList
Write-Host "已切換到 Python $version。請重新打開命令行窗口以使更改完全生效。" -ForegroundColor Green
return $true
}
# 主命令處理
if (-not $Command) {
# 交互模式
Write-Host "Python Version Manager (pyvm)"
while ($true) {
List-Versions
$selected = Read-Host "`n請輸入要切換的版本號 (輸入 q 退出)"
if ($selected -eq 'q') { exit }
$result = Use-Version $selected
if ($result) {
$continue = Read-Host "是否繼續(xù)操作其他版本?(y/n)"
if ($continue -ne 'y') { exit }
}
}
} else {
switch ($Command.ToLower()) {
'list' {
List-Versions
}
'current' {
$currentVer = Get-CurrentPythonVersion
$currentPath = Get-CurrentPythonPath
if ($currentVer) {
Write-Host "當(dāng)前 Python 版本: $currentVer"
} elseif ($currentPath) {
Write-Host "當(dāng)前使用的 Python 不在管理列表中,路徑為: $currentPath" -ForegroundColor Yellow
} else {
Write-Host "當(dāng)前未檢測到 Python 命令。" -ForegroundColor Red
}
}
'use' {
if (-not $Argument) {
Write-Error "請指定要使用的版本號。"
exit 1
}
$result = Use-Version $Argument
if (-not $result) { exit 1 }
}
default {
Write-Error "未知命令: $Command。可用命令: list, current, use <version>"
exit 1
}
}
}將上述代碼保存為 pvm.ps1,放在一個固定路徑,例如 D:\Develop\pvm\pvm.ps1。
三、將 pvm 變?yōu)槿置睿篜owerShell 函數(shù)
現(xiàn)在我們需要在 PowerShell 的配置文件中定義一個函數(shù) pvm,它負(fù)責(zé)調(diào)用 pvm.ps1 并傳遞參數(shù)。
打開 PowerShell Profile
在 PowerShell 中執(zhí)行:
notepad $PROFILE
如果提示文件不存在,選擇“是”創(chuàng)建。
添加函數(shù)定義
在打開的記事本中,輸入以下內(nèi)容(注意修改路徑為你的 pvm.ps1 實際位置):
function pvm { & "D:\Develop\pvm\pvm.ps1" @args }保存并關(guān)閉文件。
重新加載 Profile
在 PowerShell 中執(zhí)行:
. $PROFILE
四、遇到的第一道坎:執(zhí)行策略限制
執(zhí)行 . $PROFILE 時,你可能遇到這樣的錯誤:
. : 無法加載文件 D:\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1,因為在此系統(tǒng)上禁止運行腳本。有關(guān)詳細(xì)信息,請參閱 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Execution_Policies。
這是因為 PowerShell 默認(rèn)的執(zhí)行策略是 Restricted,禁止運行任何腳本(包括 profile 文件)。解決辦法是修改執(zhí)行策略。
解決方法
以管理員身份打開 PowerShell(右鍵開始菜單 → Windows PowerShell (管理員))。
執(zhí)行以下命令,將策略設(shè)置為 RemoteSigned:
Set-ExecutionPolicy RemoteSigned
如果只想對當(dāng)前用戶生效,可以加 -Scope CurrentUser:
Set-ExecutionPolicy -Scope CurrentUser RemoteSigned
按提示輸入 Y 確認(rèn)。
現(xiàn)在再次加載 profile:. $PROFILE,應(yīng)該就不會報錯了。
解釋:RemoteSigned 策略允許運行本地未簽名的腳本,但會阻止從互聯(lián)網(wǎng)下載的未簽名腳本,既滿足了我們運行本地腳本的需求,又兼顧了安全性。
五、使用 pvm 命令
一切就緒后,我們可以在 PowerShell 中愉快地使用 pvm 了。
列出所有版本:
pvm list
輸出示例:
已安裝的 Python 版本: 2.7.3 3.7.4 3.7.9 3.8.10 3.9.10 * 3.10.8 (當(dāng)前)
查看當(dāng)前版本:
pvm current
切換到指定版本(需要管理員權(quán)限):
pvm use 3.9.10
由于腳本需要修改系統(tǒng) PATH,請確保 PowerShell 是以管理員身份運行的。如果當(dāng)前不是管理員,可以右鍵點擊 PowerShell 圖標(biāo),選擇“以管理員身份運行”,然后再執(zhí)行 pvm use。
交互式模式:
pvm
進(jìn)入交互菜單,顯示版本列表并提示輸入版本號切換。
六、進(jìn)階:自動提權(quán)(可選)
每次切換都要手動以管理員身份運行 PowerShell 挺麻煩的。我們可以在腳本中集成自動提權(quán)邏輯,當(dāng)檢測到?jīng)]有管理員權(quán)限時,自動彈出 UAC 窗口以管理員身份重啟腳本。
將 pvm.ps1 開頭的權(quán)限檢查替換為以下代碼:
# 檢查并自動提權(quán)
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
Write-Host "正在請求管理員權(quán)限..." -ForegroundColor Yellow
Start-Process powershell -Verb RunAs -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$PSCommandPath`" $($MyInvocation.Line.Substring($MyInvocation.Line.IndexOf(' ') + 1))"
exit
}這樣,當(dāng)你執(zhí)行 pvm use 3.9.10 且當(dāng)前 PowerShell 不是管理員時,會自動彈出一個 UAC 確認(rèn)窗口,確認(rèn)后會在新的管理員窗口中執(zhí)行切換操作。切換完成后新窗口會關(guān)閉,原窗口仍保持。
七、常見問題
1. 切換后python --version還是舊版本?
- 請確保已經(jīng)重新打開了新的命令行窗口。修改系統(tǒng) PATH 后,已打開的程序不會自動刷新,需要重啟。
- 檢查是否處于 conda 環(huán)境中,
conda deactivate后再試。
2. 修改執(zhí)行策略后仍然報錯?
- 確認(rèn)你是在管理員 PowerShell 中修改的(如果使用
-Scope CurrentUser則無需管理員)。 - 查看當(dāng)前策略:
Get-ExecutionPolicy。 - 如果策略是
Restricted,可能是修改未生效,嘗試重新執(zhí)行。
3. 切換時提示“找不到版本”?
- 檢查
$pythonVersions中的版本號與目錄是否對應(yīng),路徑是否存在。 - 版本號大小寫敏感,建議使用數(shù)字和點號。
八、總結(jié)
通過 PowerShell 函數(shù) + 核心腳本的方式,我們成功在 Windows 上實現(xiàn)了一個輕量級的 Python 版本管理器。核心優(yōu)勢是:
- 獨立:不依賴第三方工具,完全基于 PowerShell 和系統(tǒng) API。
- 可控:代碼完全開源,可根據(jù)自己的安裝路徑隨意調(diào)整。
- 易用:
pvm list、pvm use等命令直觀,與 nvm 風(fēng)格一致。
如果你也在 Windows 下被多 Python 版本困擾,不妨試試這個方案。所有代碼已在 Windows 10/11、PowerShell 5.1 和 7+ 上測試通過。歡迎評論區(qū)交流改進(jìn)!
附:完整項目文件結(jié)構(gòu)
D:\Develop\pvm\ ├── pvm.ps1 # 核心腳本 └── README.md # 說明文檔(可選)
Profile 配置
# Microsoft.PowerShell_profile.ps1
function pvm {
& "D:\Develop\pvm\pvm.ps1" @args
}以上就是在Windows系統(tǒng)下管理多版本Python的工具實現(xiàn)方案的詳細(xì)內(nèi)容,更多關(guān)于Python多版本管理工具的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python將數(shù)據(jù)插入數(shù)據(jù)庫的代碼分享
在本篇文章里小編給大家整理的是關(guān)于python將數(shù)據(jù)插入數(shù)據(jù)庫的代碼內(nèi)容,有興趣的朋友們可以參考下。2020-08-08
Python Requests訪問網(wǎng)絡(luò)更方便
這篇文章主要介紹了使用Python Requests訪問網(wǎng)絡(luò),Python Requests 是一個非常強(qiáng)大的 HTTP 客戶端庫,用于發(fā)送 HTTP 請求,獲取響應(yīng)等操作,通過這個庫,你可以輕松地與 Web 服務(wù)進(jìn)行交互,實現(xiàn)各種網(wǎng)絡(luò)請求2024-01-01
如何使用Python和OpenCV進(jìn)行實時目標(biāo)檢測實例詳解
這篇文章介紹了一個使用Python和OpenCV實現(xiàn)實時視頻流目標(biāo)檢測的程序,程序使用預(yù)訓(xùn)練的YOLOv3模型,并通過多線程處理提高性能,代碼展示了如何導(dǎo)入庫、初始化參數(shù)、加載模型、處理視頻幀以及顯示結(jié)果,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2024-11-11
淺談Django學(xué)習(xí)migrate和makemigrations的差別
這篇文章主要介紹了淺談Django學(xué)習(xí)migrate和makemigrations的差別,具有一定借鑒價值,需要的朋友可以參考下2018-01-01
Python中私有屬性“_“下劃線和“__“雙下劃線區(qū)別
本文主要介紹了Python中私有屬性“_“下劃線和“__“雙下劃線區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-03-03

