首頁  >  文章  >  後端開發  >  Python 的內部運作原理

Python 的內部運作原理

Susan Sarandon
Susan Sarandon原創
2024-11-04 13:45:02423瀏覽

Internal Working Of Python

這是完整的程式碼檔案:程式碼

1.原始碼

當您編寫 Python 腳本時,它是人類可讀的文字。這個原始碼是一切的起點。

您的 Python 原始碼以 .py 檔案編寫,是人類可讀的。此程式碼定義了程式的功能,指定變數、函數、迴圈等。

2.編譯為字節碼(編譯器)

當你執行Python程式時,第一步是將原始碼編譯為字節碼。這是由 Python 解釋器完成的:

  • 語法檢查:確保沒有語法錯誤。
  • 編譯:將高階原始碼轉換為字節碼,這是一種較低階的、與平台無關的表示形式。該字節碼通常駐留在 __pycache__ 目錄中的 .pyc 檔案中。
    • 編譯器:Python 使用解釋器,但它首先將原始程式碼編譯為稱為字節碼的較低層級形式。
  • 標記化:將程式碼分解為稱為標記的小塊(如關鍵字、運算子、識別碼)。
  • 解析:分析標記以確保它們遵循 Python 的語法規則。
  • 控制流程圖(CFG):表示程式在執行過程中可能遍歷的所有路徑。
  • 字節碼產生:將解析後的令牌轉換為字節碼,這是Python虛擬機器(PVM)的一組指令。

Internal Working Of Python

讓我們深入探討一下。

Python 編譯器:儘管 Python 被稱為解釋性語言,但它確實有一個編譯步驟。詳細內容如下:

標記化

  • 將程式碼分解為稱為標記的小塊(如關鍵字、運算子、識別碼)。
  1. 原始碼:從您編寫的程式碼開始。
  2. 分詞器(Lexer):這會將原始碼分解為稱為標記的較小部分,例如關鍵字(for、if)、運算子(、-)、識別碼(變數名稱)和文字(例如數字或字串) .
  3. 解析:分析標記以確保它們遵循 Python 的語法規則。
  4. 語法分析:解析器取得這些標記並根據 Python 的語法規則檢查它們。
  5. 解析樹:從標記建構樹結構,表示程式碼的語法結構。
  6. 語意分析:確保程式碼在資料類型、範圍和其他特定於上下文的規則方面有意義。
  7. 控制流程圖(CFG):表示程式在執行過程中可能遍歷的所有路徑。
    • 控制流程圖:表示程式碼執行過程中可能採取的所有可能路徑。
    • 節點和邊:每個節點代表一個基本程式碼區塊,邊代表從一個區塊到另一個區塊的控制流。
  8. 字節碼產生:將解析後的令牌轉換為字節碼,這是Python虛擬機器(PVM)的一組指令。
    • 字節碼是原始程式碼的更緊湊、較低層級的表示形式,並針對執行進行了最佳化。它獨立於平台,這意味著它可以在任何具有相容 PVM 的系統上運行。
    • 字節碼:解析後的程式碼轉換為字節碼,一種較低階的、與平台無關的表示。
    • 指令集:此字節碼是Python虛擬機器(PVM)可以執行的一組指令。字節碼儲存在 __pycache__ 目錄中的 .pyc 檔案中,以加快將來的執行速度。

3.載入字節碼(字節碼)

編譯後,Python 虛擬機器載入字節碼:

  • 從快取讀取:如果字節碼之前已編譯且未更改,則從快取(__pycache__)讀取。這透過跳過編譯步驟來加快執行速度。
    • 字節碼已載入到記憶體中,準備執行。然後字節碼由 PVM 執行,解釋指令以執行程式的任務。

4.由 PVM (PVM) 執行

PVM 現在解釋並執行字節碼:

  • 指令執行:PVM 讀取每個字節碼指令並執行它。每條指令對應一個特定的操作,例如載入值、執行算術或呼叫函數。
  • 記憶體管理:管理變數和物件的記憶體分配和釋放。

Python 中的記憶體管理:

  1. 引用計數:Python 追蹤記憶體中物件的引用數量。當引用計數降至零時,可以回收該物件所佔用的記憶體。
  2. 物件分配:程式碼在運行時在記憶體中建立 Python 物件(如整數、字串、列表)。
  3. 垃圾收集:Python 有一個垃圾收集器,它透過釋放不再使用的記憶體(即引用計數為零的物件)來幫助管理記憶體。
  4. 記憶體池:Python 使用記憶體池來更有效地分配小物件。這種池化有助於減少頻繁分配和釋放小塊記憶體的開銷。
  5. 記憶體最佳化:Python 應用各種最佳化來最小化記憶體使用,例如:
    • PVM 執行各種執行時間最佳化以提高效率,例如某些實作(如 PyPy)中的即時 (JIT) 編譯。
    • 重複使用小整數和內部字串。
    • 有效管理資料結構(例如元組、列表、字典)。

範例

  • 字節碼快取:PVM 快取已編譯的字節碼,以避免每次都重新編譯原始碼。這會加快後續運轉的速度。
  • 常數折疊:這涉及在編譯時而不是運行時簡化常數表達式。例如,3 * 2 可能會預先計算為 6。

所以,總而言之:PVM 就像一個管弦樂團指揮,無縫地將字節碼轉換為電腦可以執行的操作。它的美妙之處在於,由於 PVM,Python 程式碼是可移植的,無需修改即可在不同平台上運行。

我們如何查看字節碼是否產生?

導入Python模組時,Python會將原始碼編譯為位元組碼並將其儲存在__pycache__目錄中。這有助於加快未來的導入速度,避免每次導入時都需要重新編譯模組。

流程如下:

  • 首次導入:首次導入模組時,Python 會將 .py 檔案編譯為字節碼。
  • pycache 目錄:字節碼儲存在 __pycache__ 目錄中,名稱類似 module_name.cpython-312.pyc。 # 312 是 Python 版本。
  • 後續導入:在後續導入時,Python 會檢查 __pycache__ 目錄中是否有已編譯的字節碼,如果源代碼未更改,則使用它,從而加快導入過程。

例子:

我們有 byte.py。當我們在執行 byte.py 後從 hello_world.py 導入程式碼時,我們可以看到該特定資料夾中會有一個目錄 __pycache__ ,我們可以看到 .pyc 檔案:

from hello_world import greet

greet("Byte code")


透過使用 py_compile

py_compile模組,它允許你將Python原始檔編譯成字節碼檔。這是一種加快未來運行腳本執行速度的便捷方法。

在 byte.py 中

import py_compile

py_compile.compile('hello_world.py')

  • py_compile 模組將 hello_world.py 編譯為字節碼。
  • 產生的字節碼儲存在 pycache 目錄中,建立一個名為 hello_world.cpython-38.pyc (或類似文件,取決於您的 Python 版本)的檔案。

產生字節碼:

  • 執行整個腳本來產生字節碼。這意味著任何頂級程式碼(例如 print("Hello, World!") 和 print("c"))都會在編譯過程中執行。

產生的字節碼:

  • 字節碼包含所有函數、類別和可執行語句,Python 使用它們來加速未來腳本的導入。

顯示模組

Python 中的 dis 模組用於將字節碼反組譯成更易讀的形式。這可以幫助您了解 Python 程式碼在幕後的用途。它對於調試或了解 Python 的內部結構特別有用。

  • 在internal.py中我們有
from hello_world import greet

greet("Byte code")


輸出

import py_compile

py_compile.compile('hello_world.py')

  • 程式首先導入 dis 模組,這是一個用於分析 CPython 字節碼的強大工具。 CPython 是 Python 的預設實現,字節碼是 Python 解釋器的中間語言。
  • 接下來,我定義了一個名為greet的簡單函數。此函數接受參數名稱並列印出問候語。儘管函數本身非常簡單,但 Python 底層發生的事情比表面上看起來更複雜。
  • disassemble_function 函數使用 dis.dis() 來反組譯greet函數。 dis.dis() 將 Python 函數轉換為 Python 虛擬機器實際執行的低階位元組碼。這個字節碼是Python對greet函數的解釋,更接近機器碼。
  • 當腳本呼叫 disassemble_function() 時,控制台輸出顯示我們的greet 函數的字節碼。

這是字節碼告訴我們的內容:

  • LOAD_GLOBAL(0):此操作碼用於載入全域變量,在本例中是列印函數。
  • LOAD_CONST(1):這會將常數值「Hello,」載入到堆疊上。
  • LOAD_FAST(0):此操作碼將局部變數名稱載入到堆疊上。
  • FORMAT_VALUE(0):這會格式化我們的名稱字串,準備將其插入即將建置的字串中。
  • BUILD_STRING(2):這採用堆疊上的前兩個值(「Hello,」和名稱)並建立最終字串。
  • CALL_FUNCTION(1):這一行呼叫函數(我們載入到堆疊上的全域列印函數),參數計數位於括號中(我們有一個參數,即我們的格式化字串)。
  • POP_TOP:這會刪除堆疊頂部(上一次呼叫的結果,因為 print 傳回 None)。
  • LOAD_CONST(0):不載入。
  • RETURN_VALUE:這是greet函數的回傳值,由於沒有明確的return語句,因此為None。
  • 本質上,字節碼顯示了Python執行我們的greet函數時執行的各個操作。理解這些指令對於開發人員理解 Python 如何執行程式碼、最佳化函數和管理資源至關重要 - 當我們運行 Python 程式碼時,所有這些都在幕後無縫地發生。

這不是一次令人愉快地深入 Python 機房的經歷嗎?繼續編碼並繼續探索該語言引擎室的深度? !

以上是Python 的內部運作原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn