1. 程式人生 > >python 程序、執行緒

python 程序、執行緒

一、python程式的執行原理

許多時候,在執行一個python檔案的時候,會發現在同一目錄下會出現一個__pyc__資料夾(python3)或者.pyc字尾(python2)的檔案
Python在執行時,首先會將.py檔案中的原始碼編譯成Python的byte code(位元組碼),然後再由Python Virtual Machine(Python虛擬機器)來執行這些編譯好的byte code。

1、執行流程

原始碼.py ——(編譯處理)——>位元組碼.pyc ————>python虛擬機器——(編譯)——>程式

2、編譯

執行 python demo.py 後,將會啟動 Python 的直譯器,然後將 demo.py 編譯成一個位元組碼物件 PyCodeObject。

在 Python 的世界中,一切都是物件,函式也是物件,型別也是物件,類也是物件(類屬於自定義的型別,在 Python 2.2 之前,int, dict 這些內建型別與類是存在不同的,在之後才統一起來,全部繼承自 object),甚至連編譯出來的位元組碼也是物件,.pyc 檔案是位元組碼物件(PyCodeObject)在硬碟上的表現形式。

在執行期間,編譯結果也就是 PyCodeObject 物件,只會存在於記憶體中,而當這個模組的 Python 程式碼執行完後,就會將編譯結果儲存到了 pyc 檔案中,這樣下次就不用編譯,直接載入到記憶體中。pyc 檔案只是 PyCodeObject 物件在硬碟上的表現形式。
這個 PyCodeObject 物件包含了 Python 原始碼中的字串,常量值,以及通過語法解析後編譯生成的位元組碼指令。PyCodeObject 物件還會儲存這些位元組碼指令與原始程式碼行號的對應關係,這樣當出現異常時,就能指明位於哪一行的程式碼。

3、pyc檔案

一個 pyc 檔案包含了三部分資訊:Python 的 magic number、pyc 檔案建立的時間資訊,以及 PyCodeObject 物件。

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

4、位元組碼指令

為什麼 pyc 檔案也稱作位元組碼檔案?因為這些檔案儲存的都是一些二進位制的位元組資料,而不是能讓人直觀檢視的文字資料。

Python 標準庫提供了用來生成程式碼對應位元組碼的工具 dis。dis 提供一個名為 dis 的方法,這個方法接收一個 code 物件,然後會輸出 code 物件裡的位元組碼指令資訊。

5、python虛擬機器

demo.py 被編譯後,接下來的工作就交由 Python 虛擬機器來執行位元組碼指令了。Python 虛擬機器會從編譯得到的 PyCodeObject 物件中依次讀入每一條位元組碼指令,並在當前的上下文環境中執行這條位元組碼指令。我們的程式就是通過這樣迴圈往復的過程才得以執行。

更多請參考談談 Python 程式的執行原理
推薦python虛擬機器與java虛擬機器

二、全域性直譯器鎖

全域性直譯器鎖(Global Interpreter Lock):簡稱GIL,多程序(mutilprocess) 和 多執行緒(threading)的目的是用來被多顆CPU進行訪問, 提高程式的執行效率。 但是在python內部存在一種機制(GIL),在多執行緒 時同一時刻只允許一個執行緒來訪問CPU。 GIL 並不是Python的特性,它是在實現Python解析器(CPython)時所引入的一個概念。因為CPython是大部分環境下預設的Python執行環境。所以在很多人的概念裡CPython就是Python,也就想當然的把 GIL 歸結為Python語言的缺陷。所以這裡要先明確一點:GIL並不是Python的特性,Python完全可以不依賴於GIL。例如Jython(java編寫的python直譯器)就不會存在GIL。

  • python中一個執行緒對應於c語言中的一個執行緒
  • GIL使得同一個時刻只有一個執行緒在一個cpu上執行位元組碼, 無法將多個執行緒對映到多個cpu上執行,因此無法利用cpu多核優勢
  • 大量的第三方包都是基於CPython編寫的,所以短期內想把GIL去掉不太可能