將Python應用程式及其環境打包到MS Windows上供其他用戶使用,使其能夠在任何機器上“隨時運行”,這是一項棘手的任務。這篇部落格文章描述了我個人的解決方案:我稱之為Windows版Python Bundle 的東西,它類似於虛擬環境,但在機器之間是可移植的。
Python Bundle 某種程度上位於虛擬環境、常規Python安裝和由PyInstaller或Py2exe等工具創建的獨立可執行檔提供的價值和權衡的交集處。
創造這樣的Bundle不需要新的工具。這只是一個鬆散且輕量級的約定,用於資料夾結構和一些包裝腳本,您可以輕鬆地手動建立它們。或在腳本或CI作業中自動化它們的建立。
讓我們假設我們要以自包含和「隨時運行」的方式將Python應用程式或環境打包並交付給我們的用戶。
我們可能不知道我們的使用者安裝了哪個版本的Python,或者根本沒有安裝。我們絕對不想篡改他們可能已經存在的Python安裝,其中包括不讓他們要求安裝額外的Python版本。換句話說:我們的軟體包應該是我們的用戶運行我們的應用程式或使用我們的Python環境所需的一切。
經過一番集思廣益,我們可能會想到創建一個虛擬環境(python -m venv venv_dir),將所有內容安裝到虛擬環境中,然後將虛擬環境資料夾壓縮並分發給我們的用戶。但是,我們意識到虛擬環境資料夾不容易重新定位到與其建立位置不同的路徑。此外,我們的虛擬環境還依賴用於創建它的基礎Python安裝(在該路徑下使用完全相同的Python版本)。因此,我們需要告訴我們的用戶將虛擬環境的副本放在哪裡。並且他們必須在特定路徑下安裝特定版本的Python。這不是我們想要的。
我們可以將所有需求安裝到常規Python安裝中,然後壓縮並分發其資料夾(例如,c:Program FilesPython 3.13.1),而不是虛擬環境。這大部分有效。 Windows上的Python安裝目錄通常可以重新定位到不同的路徑(在Unix上由於靜態前綴路徑而並非如此——但這是另一個主題)。
但是,有一個很大的缺陷:存在腳本可執行文件(.Scripts目錄中的.exe文件,通常在包不僅僅是庫而且還提供腳本作為入口點時由pip創建。這樣一個可執行檔案就是pip.exe本身)。這些腳本可執行檔依賴於硬編碼在其「自身」中的Python安裝路徑。
PyInstaller或Py2exe之類的工具可以將Python應用程序及其所有依賴項捆綁到單個包中。用戶無需安裝Python解釋器或任何模塊即可運行打包的應用程序。
這完美地解決了我們的分發需求。但是,權衡是我們沒有分發完整的Python環境,而是一個自定義的、簡化的Bundle格式。這可能是打包應用程序的正確工具。但是,如果我們例如想要向我們的用戶發送一個可以在IDE中使用並通過額外的pip安裝等進行擴展的“入門套件”Python環境,則它並不適用。我們正在尋找更通用的解決方案。
我們將在Powershell中開始,為我們的Bundle創建一個文件夾:
<code>mkdir bundle cd bundle</code>
然後,我們將在
<code>curl.exe -L "https://www.nuget.org/api/v2/package/python/3.13.1" -o python3.zip Expand-Archive .\python3.zip -DestinationPath extracted_nuget move .\extracted_nuget\tools python3 rm -R extracted_nuget rm -R .\python3.zip</code>
現在我們的Bundle看起來像這樣:
<code>bundle └───python3 ├───python3.exe ├───Lib/ ├───...</code>
讓我們也添加一個Scripts目錄:
<code>mkdir python3\Scripts</code>
目前我們還沒有啟用pip,所以現在讓我們這樣做。
<code>python3\python.exe -m ensurepip</code>
為了解決pip在Scripts目錄中創建依賴於硬編碼Python安裝路徑的.exe文件的問題,我們將為pip使用一個包裝腳本,該腳本修補pip的行為。
讓我們創建一個文件
<code>#!/usr/bin/python import sys import os if __name__ == "__main__": from pip._vendor.distlib.scripts import ScriptMaker ScriptMaker.executable = r"python.exe" from pip._internal.cli.main import main sys.exit(main())</code>
它是如何工作的?每當我們通過我們的包裝腳本安裝一個包(例如,python3python.exe pip_wrapperscriptspip.py
當然,這是有風險的,並且有影響。現在,我們的工作是確保當有人執行這樣的“修補的”Scripts*.exe文件時,PATH變量中的python.exe是正確的。這就是為什麼我們的Bundle需要由用戶激活,類似於虛擬環境。我們稍後會討論這一點。
(有關這種pip包裝器想法的更多信息,請參見此處)
現在,如果我們還有一個pip.exe用於我們的pip包裝器,那不是很好嗎?這樣,我們以後就可以只使用pip命令(而不必使用python pip.py)?讓我們創建一個。當然,它也需要是可移植的,這就是為什麼我們將以類似的方式創建它。
為此,讓我們創建一個
<code>mkdir bundle cd bundle</code>
然後讓我們使用python3python.exe啟動一個Python shell(REPL)並執行以下代碼來創建pip.exe。
<code>curl.exe -L "https://www.nuget.org/api/v2/package/python/3.13.1" -o python3.zip Expand-Archive .\python3.zip -DestinationPath extracted_nuget move .\extracted_nuget\tools python3 rm -R extracted_nuget rm -R .\python3.zip</code>
我們的文件夾結構現在應該如下所示:
<code>bundle └───python3 ├───python3.exe ├───Lib/ ├───...</code>
**Bundle** | **虚拟环境** | **Python安装** | **Pyinstaller** | |
**路径独立(可以复制到文件系统中的任何路径)?** | 是 | 否 (Python安装路径硬编码在虚拟环境中) | 否 (.\scripts\*.exe文件将中断) | 是 |
**可以在同一系统上有多个实例** | 是 | 是 | 没有问题 (概念是一个Python版本每个用户或系统一个Python安装) | 是 |
**磁盘使用情况** | 大 (包含完整的Python安装) | 小 (依赖于Python安装) | 大 | 中等 |
**需要激活** | 是 | 是 | 否 | 否 |
**单个可执行文件** | 否 | 否 | 否 | 是 |
**可以用作常规Python安装(REPL、pip、脚本等)** | 是 | 是 | 是 | 否 |
**可以与IDE一起使用?** | 是,但您可能需要在IDE的运行/调试配置文件中配置环境变量 | 是 | 是 | 否 |
請注意,此輸出對原文進行了改寫,但保留了原文的全部信息和圖片。 我使用了更流暢的表達方式,並對一些段落進行了重新組織,使其更易於理解。 圖片的格式保持不變。
以上是Windows 上的可攜式 Python 套件的詳細內容。更多資訊請關注PHP中文網其他相關文章!