通過Python運行原理了解 .pyc

通過Python運行原理了解 .pyc

由於最近一位同學在做分佈式計算時,部分模塊只把pyc文件拷貝部署至遠程計算節點,導致主節點程序更新後,計算節點出現錯誤異常,於是有了這篇文章...

小栗子

有兩個py模塊,test.py與import_test.py模塊。

通過Python運行原理了解 .pyc

test.py

mport_test.py模塊中導入了test模塊,如下圖

通過Python運行原理了解 .pyc

import_test.py

我們先執行test.py,看下目錄,無變化。

通過Python運行原理了解 .pyc

執行test.py後目錄

然後再執行import_test.py。

通過Python運行原理了解 .pyc

執行import_test.py後目錄

可以發現,在執行import_test.py之後,目錄中多了一個test.pyc文件,這是為神馬呢。

通過Python運行原理了解 .pyc

Python運行原理

Python運行原理

每一個 .py 文件都會作為Python中的一個模塊,如上例子中,包含了test、import_test兩個模塊,其中import_test作為程序運行的入口(導入了test模塊)。

運行import_test.py後,會激活 Python 的解釋器,將test模塊編譯成一個字節碼對象 (PyCodeObject)並保存在內存當中(方便CPU讀取,提高程序運行效率),接下來由 Python 虛擬機來執行字節碼指令。當Python程序運行結束時,Python解釋器則將字節碼對象持久化至pyc文件中(.pyc 文件是字節碼對象在硬盤上的表現形式)。

當此模塊程序再次運行時,首先程序會在硬盤中尋找pyc文件,如果找到,則直接將字節碼對象加載到內存中,否則就重複上面的過程。

也就是說,我們之所以要把py文件編譯成pyc文件,其最大的好處在於我們在重複運行程序時,不需要重新對該模塊進行重複的編譯。

那麼這個字節碼對象(PyCodeObject) 有包含什麼?

一個字節碼對象文件(.pyc)包含了三部分信息:Python 的 magic number、創建時間以及 PyCodeObject 對象。

magic number 是 Python 定義的一個整數值。一般來說,不同版本的 Python 實現都會定義不同的 magic number,這個值是用來保證 Python 兼容性的。不同版本的 Python 定義的字節碼指令可能會不同,如果不做檢查,執行的時候就可能出錯。比如要限制由低版本編譯的 pyc 文件不能讓高版本的 Python 程序來執行,只需要檢查 magic number 不同就可以了。

創建時間是用來判斷pyc文件是否有效,每次在載入之前都會先校驗py文件和pyc文件的最後修改日期,如果不一致則重新生成一份pyc文件(詳見import.py),避免了修改源代碼後與本地字節碼文件產生衝突。

那麼為什麼執行test.py模塊時,沒有生成.pyc文件呢?

首先,既然pyc存在意義是重複運行的時候免去編譯操作,那麼需要編譯成pyc文件的應該是那些可以重用的模塊。所以Python的解釋器認為:只有import進來的模塊,才是需要被重用的模塊。這也是為什麼執行import_test.py模塊時,只生成了被導入的test.py模塊的pyc文件的原因。

日常程序設計時,公共通用的方法封裝成單獨的模塊,通過導入模塊被使用,有利於提高代碼運行效率,當然這一般也不會是什麼瓶頸。。。


分享到:


相關文章: