首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Python 程式執行原理深度解析:從原始碼到位元組碼虛擬機

     編輯:更多程式設計知識

執行流程總覽

  1. 原始碼 (.py) → 編譯器 → 位元組碼 (PyCodeObject)
  2. 位元組碼 → PVM (Python Virtual Machine) → 逐條解釋執行

核心概念

1. 位元組碼

  • 在 PVM 內部對應 PyCodeObject C 結構體。
  • .pyc 檔案是位元組碼在磁碟上的序列化形式。

2. .pyc 檔案生成機制

  • 觸發時機: import 模組時。
  • 直接執行腳本: python test.py 會編譯並執行,但不生成 test.pyc。
  • 被匯入模組: import util 會生成 util.pyc。
  • 手動生成: python -m py_compile test.py。
  • 載入策略: 同時存在 .py 和 .pyc 時,比對修改時間,.py 較新則重新編譯。

3. PyCodeObject 結構體關鍵欄位 (C 定義)

typedef struct {
  PyObject_HEAD
  int co_argcount;        // 位置參數個數
  int co_nlocals;         // 區域變數個數
  int co_stacksize;       // 堆疊大小
  int co_flags;
  PyObject *co_code;      // 位元組碼指令序列 (bytes)
  PyObject *co_consts;    // 常數表 (元組)
  PyObject *co_names;     // 符號名稱表 (全域/屬性名)
  PyObject *co_varnames;  // 區域變數名稱表
  PyObject *co_freevars;  // 閉包自由變數
  PyObject *co_cellvars;  // 供內層函數引用的變數
  PyObject *co_filename;  // 原始碼檔名
  PyObject *co_name;      // 模組/函數/類別名稱
  int co_firstlineno;     // 起始行號
  PyObject *co_lnotab;    // 位元組碼偏移量對應行號表
  void *co_zombieframe;   // 最佳化用
} PyCodeObject;

4. .pyc 檔案格式

pyc 檔案格式

包含:Magic Number (版本標識) + Timestamp (修改時間) + Marshaled PyCodeObject。

實戰:剖析位元組碼

範例程式 test.py

s = "hello"
def func():
    print(s)
func()

互動式環境編譯檢視

>>> code = compile(open('test.py').read(), 'test.py', 'exec')
>>> code.co_argcount
0
>>> code.co_names
('s', 'func')
>>> code.co_varnames
('s', 'func')
>>> code.co_consts
('hello', <code object func at 0x...>, None)
>>> code.co_code
'd\x00\x00Z\x00\x00d\x01\x00\x84\x00\x00Z\x01\x00e\x01\x00\x83\x00\x00\x01d\x02\x00S'

巢狀函數 func 的 PyCodeObject

>>> func_code = code.co_consts[1]
>>> func_code.co_argcount
0
>>> func_code.co_names
('s',)
>>> func_code.co_consts
(None,)
>>> func_code.co_code
't\x00\x00GHd\x00\x00S'

指令序列解析

co_code 為位元組流,格式:[opcode, oparg_low, oparg_high, ...]。

  • Opcode: 1 字節,對應操作碼 (如 LOAD_CONST=100)。
  • Oparg: 2 字節 (小端序),通常為 co_consts 或 co_names 索引。

反組譯工具 dis 模組

>>> import dis
>>> dis.dis(code)
  1           0 LOAD_CONST               0 ('hello')
              3 STORE_NAME               0 (s)
  2           6 LOAD_CONST               1 (<code object func>)
              9 LOAD_CONST               2 ('func')
             12 MAKE_FUNCTION            0
             15 STORE_NAME               1 (func)
  4          18 LOAD_NAME                1 (func)
             21 CALL_FUNCTION            0 (0 positional, 0 keyword pair)
             24 POP_TOP
             25 LOAD_CONST               3 (None)
             28 RETURN_VALUE

虛擬機執行模型

1. 幀物件 PyFrameObject

模擬 C 執行時的堆疊幀,鏈結形成呼叫堆疊。

typedef struct _frame {
  PyObject_VAR_HEAD
  struct _frame *f_back;      // 呼叫者幀
  PyCodeObject *f_code;       // 對應的位元組碼
  PyObject *f_builtins;       // 內建命名空間
  PyObject *f_globals;        // 全域命名空間
  PyObject *f_locals;         // 區域命名空間
  PyObject **f_valuestack;    // 運算堆疊底
  PyObject **f_stacktop;      // 運算堆疊頂
  ...
} PyFrameObject;

2. 執行範例追蹤

模組層級幀 執行 test.py 位元組碼:

  • LOAD_CONST 0 ('hello') → 推入堆疊
  • STORE_NAME 0 (s) → 彈出堆疊,存入 f_locals['s']
  • LOAD_CONST 1 (func_code) → 推入
  • MAKE_FUNCTION → 建立函數物件,綁定 func_code 及閉包環境
  • STORE_NAME 1 (func) → 存入區域
  • LOAD_NAME 1 (func) → 取得函數物件
  • CALL_FUNCTION → **建立新幀** 執行 func_code

函數幀 執行 func:

  • LOAD_GLOBAL 0 (print) → 從 f_globals 找 print
  • LOAD_DEREF 0 (s) → 從閉包 (f_locals 的 cell) 取 s
  • CALL_FUNCTION 1 → 呼叫 print(s)
  • RETURN_VALUE → 返回 None,銷毀幀

3. 運行時堆疊視覺化

Python 運行時堆疊

進階:檢查當前幀

import sys

def func():
    frame = sys._getframe()
    print("Locals:", frame.f_locals)
    print("Globals keys:", list(frame.f_globals.keys())[:5])
    print("Caller locals:", frame.f_back.f_locals)
    print("s =", frame.f_locals['s'])

s = "hello"
func()

總結

Python 採用「編譯為位元組碼 + 棧式虛擬機解釋執行」架構。理解 PyCodeObject、PyFrameObject、指令集與命名空間機制,有助於效能調優、除錯、以及開發工具鏈 (如型別檢查器、JIT 編譯器)。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved