在日常生活中,Python代碼一般是不編譯的,幾個py文件復制來就能用。再加上腳本語言的名頭,有些不太了解Python的朋友就以為Python沒有編譯這個過程。其實,雖然Python是腳本語言,但它與Java和C#一樣,只能執行字節碼。只是Python將編譯過程隱藏起來,不大明顯而已。今天這篇筆記詳細記述一下Python的編譯過程以及一些技巧。
我們一般使用這樣的命令運行一個python程序:
C:\> python im.py其中,im.py文件的內容是:
#!/usr/bin/env pythonimport mymodulemymodule.say("hello")執行這一命令后,Python解釋器其實并不一定會讀入文件,它會嘗試讀取文件或者文件。如果都失敗了,或者文件比/新,才轉而讀入文件。Python只懂得解釋執行字節碼,所以文件讀入后第一件事就是先進行編譯。編譯的結果會被嘗試保存到文件中。等一下會討論如何控制Python的編譯過程。
雖然與C++/Java這樣的靜態語言一樣,采用了編譯(compile)這個術語,實際情況還是有所不同的。首先,最明顯的一點,Python編譯的最終結果不是機器碼,而是字節碼。Python的編譯實際上主要是進行文法分析,生成一個抽象語法樹,然后轉儲成字節碼形式了事。
從上面的介紹可以發現Python的編譯與C++相去甚遠。不過倒與Java有些相像,因為它們都生成字節碼。憑良心說,Java的編譯過程比Python先進很多。Java的解釋器在執行Java字節碼的時候,會使用JIT,將循環操作等熱點轉化成為機器碼。所以有時候Java的性能能夠達到接近C++的級別。Python缺少JIT并非故意所為,而是缺少人力資源。現在已經有一個pysco的外部模塊,據說能大大提高Python代碼的速度,不過這個模塊還沒有進入Python的官方代碼。
Python的字節碼列表可以見此鏈接:http://www.python.org/doc/2.5.2/lib/bytecodes.html
接下來,介紹一下如何"稍微"控制Python的編譯過程。只所以說是稍微,是因為無論何種情況,Python都會對字節碼進行一些簡單優化(basic Peephole Optimization,詳情參見Python的源代碼。2.5版本的Python位于 ,2.7版本則位于)。這些優化不能通過環境變量或者命令參數將其禁用。比如:
if True: return 1else: return 0會被優化成為:
return 1更多的優化正被添加到Python源代碼內。只有三個參數能影響編譯時的優化:
去除所有的語句,并將這個內置變量的值設置為。方法是運行Python的時候在命令行添加參數:
python -O im.py除了第一條所做的事情,還去除所有docstring。方法是運行Python的時候在命令行添加參數:
python -OO im.py默認的,對于一個模塊,編譯后的字節碼會被保存到與源代碼相同的文件夾內。這樣就可以加速模塊的載入速度。大多數使用Python的朋友們都寫過包含兩三個文件的程序。通常可以發現除了.py文件之外,文件夾內還會有.pyc文件。文件即是的字節碼。如果運行Python的命令行包含了或者參數,Python會將優化后的字節碼保存到文件。想要禁止生成.pyc或者.pyo文件,可以在運行Python的時候,在命令行里添加參數:
python -B im.py還可以設置環境變量:
c:\> set PYTHONDONTWRITEBYTECODE=x
看完上面三條說明,有的朋友可能會疑問,"-O"和"-OO"參數真的就干那三件事?不幸的是,還真是如此。至少在Python2.5的時候就是這樣。所以加"-O"參數并不能明顯優化Python的運行速度。這兩個選項的真正作用是區分調試版本和發行版本。在程序中可以盡量多增加一些語句,以便程序員在調試階段發現一些隱藏的錯誤。而在發布時,將這些語句去除。如果你的軟件是商業軟件的話,加上"-OO"參數可以讓別人看不清內部函數的用途,增加一些破解的難度。有了這些,誰還說Python不能寫商業軟件?
說到商業軟件,不得不說到反編譯工具。很神奇的一件事,Python內置了反編譯的模塊!囧rz,使用方法也很簡單:
>>> import mymodule >>> import dis >>> dis.dis(mymodule) #打印出反編譯的結果反編譯后的代碼與匯編語言接近,所以想要破解Python商業軟件還是有一定難度的。據說還有一些工具能夠反編譯出漂亮的Python源代碼,我并沒有親見。
--------------河南新華