Nuitka簡介:編譯和分發Python的更好方法
譯文譯者 | 李睿
審校 | 孫淑娟
隨著Python越來越受歡迎,其局限性也越來越明顯。一方面,編寫Python應用程序并將其分發給沒有安裝Python的人員可能非常困難。
解決這一問題的最常見方法是將程序與其所有支持庫和文件以及Python運行時打包在一起。有一些工具可以做到這一點,例如PyInstaller,但它們需要大量的緩存才能正常工作。更重要的是,通常可以從生成的包中提取Python程序的源代碼。在某些情況下,這會破壞交易。
第三方項目Nuitka提供了一個激進的解決方案。它將Python程序編譯為C語言二進制文件——不是通過將CPython運行時與程序字節碼打包,而是通過將Python指令翻譯成C語言。其結果能夠以壓縮包的形式分發,也可以與其他第三方產品一起打包到安裝程序中。
Nuitka還試圖保持與Python生態系統的最大兼容性,因此NumPy等第三方庫可以可靠地工作。Nuitka還盡可能地對編譯后的Python程序進行性能改進,但同樣不會犧牲整體兼容性。但不能保證加快速度,因此它們在工作負載之間變化很大,并且某些程序可能不會體驗到任何顯著的性能改進。一般來說,最好不要依賴Nuitka來提高性能,而是作為捆綁解決方案。
安裝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之后,使用Nuitka或python-m nuitka調用它。
開發人員想要使用Nuitka做的第一件事是驗證整個工具鏈是否正常工作,包括C編譯器。要對此進行測試,可以編譯一個簡單的“Hello world”Python程序,將其命名為main.py:
使用Nuitka編譯Python程序時,將入口點模塊的名稱作為參數傳遞給Nuitka,例如Nuitka main.py。當這樣調用時,Nuitka將接收main.py并從中構建一個可執行文件。
需要注意的是,因為只是在測試Nuitka的功能,所以它只會將該Python文件編譯為可執行文件。它不會編譯任何其他內容,也不會捆綁任何內容以進行重新分發。但是編譯一個文件應該足以確定Nuitka的工具鏈是否設置正確。
在編譯完成后,應該會看到與Python程序位于同一目錄中的二進制可執行文件。運行可執行文件以確保其正常工作。
還可以通過將--run作為命令行標志傳遞自動運行Nuitka編譯的應用程序。
如果“Hello world”測試可執行文件有效,可以嘗試將其打包為可再發行文件。以下解釋這個過程。
需要注意的是,當使用Nuitka運行第一個測試編譯時,它可能會在幾秒鐘內完成。而這只編譯一個模塊,而不是整個程序。使用Nuitka編譯完整的程序可能需要幾分鐘或更長時間,具體取決于程序使用的模塊數量。
使用Nuitka編譯Python程序
在默認情況下,Nuitka只編譯指定的模塊。如果模塊有來自程序中其他地方、標準庫或第三方包的導入,則需要指定也應該編譯這些導入。
考慮修改后的“Hello world”程序,其中有一個名為greet.py的相鄰模塊:
和修改后的main.py:
要編譯這兩個模塊,可以使用--follow-imports開關:
該開關確保整個程序所需的所有導入都從導入語句中跟蹤并一起編譯。
另一個選項--nofollow-import-to允許從導入過程中排除特定的子目錄。這一選項對于篩選出知道從未使用過的測試套件或模塊很有用。它還允許提供通配符作為參數。
圖1.使用Nuitka編譯大型復雜程序。這個示例涉及編譯Pyglet模塊以及標準庫中的許多模塊,這需要幾分鐘的時間
(1)包括動態導入
現在出現了Python用戶在嘗試打包Python應用程序以進行分發時經常遇到的問題之一。--follow-imports選項僅遵循通過import語句在代碼中顯式聲明的導入。它不處理動態導入。
為了解決這個問題,可以使用--include-plugin-directory開關為動態導入的模塊提供一個或多個路徑。例如,對于包含動態導入代碼的名為mods的目錄,可以使用:
(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可以找到它),以及它下面的所有內容:
如果包需要自己的數據文件,可以使用--include-package-data選項包含這些文件:
該命令告訴Nuitka獲取包目錄中實際上不是代碼的所有文件。
如果只想包含單個模塊,可以使用--include-module:
該命令告訴Nuitka只包含mypackage.mymodule,而不包含其他內容。
編譯Python程序進行重新分發
當想用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