譯者 | 李睿
審校 | 孫淑娟
隨著Python越來越受歡迎,其限制也越來越明顯。一方面,編寫Python應用程式並將其分發給沒有安裝Python的人員可能非常困難。
解決這個問題的最常見方法是將程式與其所有支援庫和檔案以及Python運行時打包在一起。有一些工具可以做到這一點,例如PyInstaller,但它們需要大量的快取才能正常運作。更重要的是,通常可以從產生的套件中提取Python程式的原始碼。在某些情況下,這會破壞交易。
第三方專案Nuitka提供了一個激進的解決方案。它將Python程式編譯為C語言二進位檔案-不是透過將CPython執行時間與程式字節碼打包,而是透過將Python指令翻譯成C語言。其結果能夠以壓縮包的形式分發,也可以與其他第三方產品一起打包到安裝程序中。
Nuitka也試圖保持與Python生態系統的最大相容性,因此NumPy等第三方函式庫可以可靠地運作。 Nuitka也盡可能地對編譯後的Python程式進行效能改進,但同樣不會犧牲整體相容性。但不能保證加快速度,因此它們在工作負載之間變化很大,並且某些程式可能不會體驗到任何顯著的效能改進。一般來說,最好不要依賴Nuitka來提高效能,而是作為捆綁解決方案。
Nuitka可以與Python 2.6到2.7和Python3.3到3.10配合使用。它可以為Microsoft Windows、macOS、Linux和FreeBSD/NetBSD編譯二進位。需要注意的是,開發人員必須在目標平台上建置二進位檔案;不能交叉編譯。
對於每個平台,除了需要Python運行時外,還需要一個C編譯器。在Microsoft Windows上,建議使用Visual Studio 2022或更高版本,但也可以使用MinGW-w64 C11(gcc 11.2或更高)。對於其他平台,可以在Visual Studio下的Windows上使用gcc 5.1或更高版本g 4.4或更高階、clang或clang cl。
要注意的是,如果使用Python 3.3或Python 3.4,由於工具依賴性,將需要Python 2.7。如果可以的話,所有這些都應該成為使用最新版本Python的理由。
最好將Nuitka與專案一起安裝在虛擬環境中,作為開發依賴項而不是分發依賴項。 Nuitka本身並未與項目捆綁在一起或被其項目使用;它執行捆綁。
在安裝Nuitka之後,使用Nuitka或python-m nuitka呼叫它。
開發人員想要使用Nuitka做的第一件事是驗證整個工具鏈是否正常運作,包括C編譯器。要對此進行測試,可以編譯一個簡單的“Hello world”Python程序,將其命名為main.py:
print ("Hello world")
使用Nuitka編譯Python程式時,將入口點模組的名稱會作為參數傳遞給Nuitka,例如Nuitka main.py。當這樣呼叫時,Nuitka將接收main.py並從中建構一個可執行檔。
要注意的是,因為只是在測試Nuitka的功能,所以它只會將該Python檔案編譯為執行檔。它不會編譯任何其他內容,也不會捆綁任何內容以進行重新分發。但是編譯一個檔案應該足以確定Nuitka的工具鏈是否設定正確。
在編譯完成後,應該會看到與Python程式位於相同目錄中的二進位執行檔。運行可執行檔以確保其正常工作。
也可以透過將--run作為命令列標誌傳遞自動執行Nuitka編譯的應用程式。
如果「Hello world」測試執行檔有效,可以嘗試將其打包為可再發行檔案。以下解釋這個過程。
要注意的是,當使用Nuitka執行第一個測試編譯時,它可能會在幾秒鐘內完成。而這只編譯一個模組,而不是整個程式。使用Nuitka編譯完整的程式可能需要幾分鐘或更長時間,具體取決於程式使用的模組數量。
在預設情況下,Nuitka只編譯指定的模組。如果模組有來自程式中其他地方、標準函式庫或第三方套件的導入,則需要指定也應該編譯這些導入。
考慮修改後的「Hello world」程序,其中有一個名為greet.py的相鄰模組:
def greet(name): print ("Hello ", name)
和修改後的main.py:
import greet greet.greet("world")
要編譯這兩個模組,可以使用--follow-imports開關:
nuitka --follow-imports main.py
該開關確保整個程式所需的所有導入都從導入語句中追蹤並一起編譯。
另一個選項--nofollow-import-to允許從匯入過程中排除特定的子目錄。此選項對於篩選出知道從未使用過的測試套件或模組很有用。它還允許提供通配符作為參數。
图1.使用Nuitka编译大型复杂程序。这个示例涉及编译Pyglet模块以及标准库中的许多模块,这需要几分钟的时间
(1)包括动态导入
现在出现了Python用户在尝试打包Python应用程序以进行分发时经常遇到的问题之一。--follow-imports选项仅遵循通过import语句在代码中显式声明的导入。它不处理动态导入。
为了解决这个问题,可以使用--include-plugin-directory开关为动态导入的模块提供一个或多个路径。例如,对于包含动态导入代码的名为mods的目录,可以使用:
nuitka--follow-imports--include-plugin-directory=modsmain.py
(2)包括数据文件和目录
如果Python程序使用在运行时加载的数据文件,Nuitka也无法自动检测这些文件。要将单个文件和目录包含在Nuitka打包程序中,可能使用--include-data-files和--include-data-dir。
--include-data-files允许为要复制的文件指定通配符以及要将它们复制到的位置。例如,--include-data dir=/path/to/data=data会将/path.to/data中的所有内容复制到分发目录中的匹配目录数据。
-include-data-dir的工作方式大致相同,只是它不使用通配符;它只允许传递要复制的路径和要将其复制到的分发文件夹中的目标。例如,--include-data dir=/path/to/data=data会将/path.to/data中的所有内容复制到分发目录中的匹配目录数据。
(3)包括Python包和模块
指定导入的另一种方法是使用Python样式的包命名空间而不是文件路径,使用--include-package选项。例如,以下命令将包括mypackage,它在磁盘上的任何位置(假设Python可以找到它),以及它下面的所有内容:
nuitka --include-package=mypackage main.py
如果包需要自己的数据文件,可以使用--include-package-data选项包含这些文件:
nuitka --include-package=mypackage --include-package-data=mypackage main.py
该命令告诉Nuitka获取包目录中实际上不是代码的所有文件。
如果只想包含单个模块,可以使用--include-module:
nuitka --include-module=mypackage.mymodule main.py
该命令告诉Nuitka只包含mypackage.mymodule,而不包含其他内容。
当想用Nuitka编译Python程序以进行重新分发时,可以使用命令行开关--standalone来处理大部分工作。此开关自动跟随所有导入并生成一个dist文件夹,其中包含已编译的可执行文件和所需的任何支持文件。要重新分发程序,只需要复制此目录即可。
不要指望--standalone的程序在第一次运行时就可以工作。Python程序的一般动态性几乎保证了需要使用上述其他一些选项来确保编译的程序正常运行。例如,如果有一个需要特定字体的GUI应用程序,可能必须使用--include-data-files或--include-data-dir将它们复制到发行版中。
此外,如上所述,--standalone应用程序的编译时间可能比测试编译长得多。一旦对测试独立构建的应用程序需要多长时间有所了解,就为测试独立构建的应用程序所需的构建时间进行预算。
最后,Nuitka提供了另一个构建选项--onefile。对于那些熟悉PyInstaller的人来说,--onefile的工作方式与该程序中的相同选项相同:它将整个应用程序(包括其所有依赖文件)压缩为单个可执行文件,无需重新分发其他文件。但是,重要的是要知道--onefile在Linux和Microsoft Windows上的工作方式不同。在Linux上,它使用存档的内容安装一个虚拟文件系统。在Windows上,它会将文件解压缩到一个临时目录中并从那里运行它们,它必须为程序的每次运行执行这一操作。在Windows上使用--onefile可能会显著降低启动程序所需的时间。
原文标题:Intro to Nuitka: A better way to compile and distribute Python,作者:Serdar Yegulalp
以上是Nuitka簡介:編譯和分發Python的更好方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!