python源碼是如何運行起來的

為什么要了解底層原理

  • 寫出高質量代碼

  • 問題定位

  • 滿足好奇心

  • 機械通感

開始

當我們編寫并運行一行?print('Hello, World!')?時,背后究竟發生了什么?Python 代碼是如何從我們可讀的文本,變成計算機可以執行的指令的呢?

很多人將 Python 稱為一門“解釋型語言”,但這其實不完全準確。更精確地說,Python 是一門先編譯后解釋的語言,這點與java其實非常相似。

它的運行過程涉及兩個核心階段:

  1. 編譯階段:Python 解釋器首先將我們編寫的源代碼(.py?文件)編譯成一種稱為字節碼 (Bytecode)?的中間形式。

  2. 解釋階段:編譯產生的字節碼隨后由?Python 虛擬機 (Python Virtual Machine, PVM)?來解釋執行。

Part.1 從 Python 源代碼到字節碼

Python 代碼的生命周期始于我們編寫的?.py?文件。當我們執行一個 Python 腳本時,解釋器并不直接逐行解析這些文本。相反,它會先進行一個“預處理”步驟——編譯成字節碼。

什么是字節碼?

字節碼是一種低級、與平臺無關的指令集。它不像機器碼那樣是給物理 CPU 執行的,而是專門為 Python 虛擬機 (PVM) 設計的。這個設計使得 Python 代碼擁有了出色的可移植性——同一份 Python 代碼可以在任何安裝了 Python 解釋器的操作系統上運行,因為字節碼是標準化的。

這個過程主要分為兩個核心步驟:詞法分析 (Lexical Analysis)?和?語法分析 (Parsing)。可以將它類比為我們閱讀句子的過程:首先識別出每個單詞(詞法分析),然后理解整個句子的語法結構(語法分析)。

核心流程:

源代碼 -> [詞法分析器] -> 令牌流 -> [語法分析器] -> 抽象語法樹 (AST)->字節碼

第 1 步:詞法分析 (Lexical Analysis) - 將代碼分解為“token”

當 Python 解釋器拿到您的源代碼(一串純文本字符)時,它做的第一件事不是去理解代碼的邏輯,而是先將其打碎成一個個有意義的最小單元。這些單元被稱為 “令牌” (Token)。

一個令牌通常包含兩部分信息:

類型:例如 NAME (名稱/標識符), NUMBER (數字), OP (操作符), KEYWORD (關鍵字)。

值:這個令牌對應的具體文本,例如 result, 10, +。

舉個例子,對于我們之前用的代碼 result = a + b,詞法分析器會掃描這段文本,并生成類似下面這樣的一個“令牌流”:

NAME: 'result'
OP: '='
NAME: 'a'
OP: '+'
NAME: 'b'

您可以把這個階段看作是給代碼“斷詞”。Python 內置的 tokenize 模塊可以讓我們親眼看到這個過程:

import tokenize
from io import BytesIOcode = """
result=a+b
"""tokens = tokenize.tokenize(BytesIO(code.encode('utf-8')).readline)for token in tokens:print(token)

結果

TokenInfo(type=63 (ENCODING), string='utf-8', start=(0, 0), end=(0, 0), line='')
TokenInfo(type=62 (NL), string='\n', start=(1, 0), end=(1, 1), line='\n')
TokenInfo(type=1 (NAME), string='result', start=(2, 0), end=(2, 6), line='result=a+b\n')
TokenInfo(type=54 (OP), string='=', start=(2, 6), end=(2, 7), line='result=a+b\n')
TokenInfo(type=1 (NAME), string='a', start=(2, 7), end=(2, 8), line='result=a+b\n')
TokenInfo(type=54 (OP), string='+', start=(2, 8), end=(2, 9), line='result=a+b\n')
TokenInfo(type=1 (NAME), string='b', start=(2, 9), end=(2, 10), line='result=a+b\n')
TokenInfo(type=4 (NEWLINE), string='\n', start=(2, 10), end=(2, 11), line='result=a+b\n')
TokenInfo(type=0 (ENDMARKER), string='', start=(3, 0), end=(3, 0), line='')

類型名

描述

NAME

變量名、關鍵字等

NUMBER

數值

STRING

字符串

OP

運算符

INDENT

縮進

DEDENT

取消縮進

NEWLINE

語句結束

ENCODING

文件編碼(如 utf-8)

COMMENT

注釋

NL

非邏輯新行

ENDMARKER

文件結束標記

第2步:語法分析

現在我們有了一個扁平的、線性的令牌列表,但解釋器還不知道它們之間的關系。這時 解析器 (Parser) 就登場了。

解析器的任務是接收詞法分析器生成的令牌流,并根據 Python 語言預設的語法規則 (Grammar),將這些令牌組織成一個具有層級結構的樹。這個樹就是抽象語法樹 (AST)。

繼續我們的例子 result = a + b:

解析器拿到 NAME, OP('='), NAME, OP('+'), NAME 這個令牌序列。

它根據 Python 的語法規則,識別出這是一個“賦值語句”的模式。

于是它創建一個 Assign (賦值) 節點作為根。

語法規定,= 左邊的是賦值目標 (target),于是它把第一個 NAME('result') 令牌作為 Assign 節點的 targets 子節點。

= 右邊的 a + b 是要賦的值 (value)。解析器接著分析 NAME('a'), OP('+'), NAME('b') 這部分。

它識別出這是一個“二元運算” (Binary Operation) 的模式。

于是它創建一個 BinOp 節點,并將其作為 Assign 節點的 value 子節點。

語法規定,+ 是操作符 (op),a 是左操作數 (left),b 是右操作數 (right)。解析器相應地創建 Add、Name('a') 和 Name('b') 節點,并將它們作為 BinOp 節點的子節點。

經過這一系列操作,一個扁平的令牌流就變成了一個能清晰反映代碼邏輯和結構的樹狀數據結構——AST。這個 AST 完整地表達了“將變量 a 和 b 相加的結果,賦值給變量 result”這一操作,并且已經去除了所有不影響語法的細節(如空格)。 將 AST(抽象語法樹)轉化為字節碼的過程,是由?Python 的編譯器 (Compiler)?完成的。這個過程可以被理解為一個**“翻譯”**工作:編譯器讀取結構化的 AST,并將其翻譯成一個線性的、供 Python 虛擬機 (PVM) 執行的指令列表。

這個“翻譯”過程的核心思想是?“樹的遍歷” (Tree Traversal)。

第3步:遍歷 AST 并生成指令

編譯器會從 AST 的根節點開始,以深度優先的方式遞歸地訪問(或稱“遍歷”)樹中的每一個節點。對于每一種類型的節點(如?Assign,?BinOp,?Name?等),編譯器都有一個專門的處理函數。這個設計模式通常被稱為?訪問者模式 (Visitor Pattern)。

讓我們以我們熟悉的?result = a + b?為例,看看編譯器是如何“走”過它的 AST 并生成字節碼的。

import ast# 我們要分析的簡單代碼行
code_line = """
# 這是一個我們編寫的簡單函數
result = a + b
"""# 解析代碼并獲取 AST
tree = ast.parse(code_line)# ast.dump() 可以用一種開發者友好的方式打印出樹的結構
print(ast.dump(tree, indent=4))

這是它的 AST 結構:

Assign(targets=[Name(id='result')],value=BinOp(left=Name(id='a'),op=Add(),right=Name(id='b'))
)

編譯器的工作流程如下:

  1. 訪問?Assign?(賦值) 節點

    • 編譯器的規則是:要完成一次賦值,必須先計算出右邊的值。

    • 因此,它不會立即生成賦值指令,而是遞歸地去訪問?Assign?節點的?value?子節點,也就是?BinOp?節點。

  2. 訪問?BinOp?(二元運算) 節點

    • 編譯器的規則是:要計算一個二元運算,必須先得到左右兩個操作數的值。

    • 它首先遞歸地訪問?left?子節點,也就是?Name(id='a')

  3. 訪問?Name(id='a')?節點

    • 編譯器看到這是一個變量名,并且當前上下文是需要“加載”它的值。

    • 于是,它生成第一條字節碼指令:LOAD_FAST a。這條指令的作用是:在程序運行時,從本地變量中找到?a?的值,并將其推到 PVM 的操作數堆棧頂部。

    • a?節點處理完畢,返回到?BinOp?節點的訪問流程。

  4. 回到?BinOp?節點

    • 左邊已經處理完。現在,編譯器遞歸地訪問?right?子節點,也就是?Name(id='b')

  5. 訪問?Name(id='b')?節點

    • 和處理?a?一樣,編譯器生成第二條字節碼指令:LOAD_FAST b。運行時,b?的值會被推到堆棧頂部。

    • b?節點處理完畢,返回到?BinOp?節點的訪問流程。

  6. 再次回到?BinOp?節點

    • 現在,左右兩個子節點都處理完了。編譯器知道,在運行時,a?和?b?的值會依次位于操作數堆棧上。

    • 它查看操作符?op?是?Add()

    • 于是,它生成第三條字節碼指令:BINARY_ADD。這條指令會從堆棧彈出頂部的兩個值,將它們相加,然后把結果再推回堆棧頂部。

    • BinOp?節點處理完畢,返回到?Assign?節點的訪問流程。

  7. 再次回到?Assign?節點

    • 現在,value?子節點(即?a + b)已經完全處理完。編譯器知道,在運行時,a + b?的計算結果正位于操作數堆棧的頂部。

    • 它查看賦值目標?targets?是?Name(id='result')

    • 于是,它生成第四條字節碼指令:STORE_FAST result。這條指令會從堆棧頂部彈出那個計算結果,并將其存入名為?result?的本地變量中。

    • Assign?節點處理完畢。

至此,result = a + b?這行代碼的 AST 就被完整地遍歷并轉換成了字節碼序列:

LOAD_FAST     a
LOAD_FAST     b
BINARY_ADD
STORE_FAST    result

Part.2 解釋執行

當字節碼準備就緒后,就輪到 Python 虛擬機 (PVM) 登場了。PVM 是 Python 的運行時引擎,是真正執行代碼的地方。它是一個在您的操作系統上運行的軟件程序,它模擬了一臺“理想”的計算機,專門用來執行 Python 字節碼。

PVM 的核心可以理解為一個巨大的?switch?循環,我們稱之為評估循環 (Evaluation Loop)?或解釋器循環。這個循環不斷地做三件事:

  1. 讀取下一條字節碼指令。

  2. 根據指令的類型,執行相應的操作。

  3. 重復此過程,直到沒有指令為止。

PVM 是一個堆棧機 (Stack-based machine)。這意味著它使用一種叫做“調用堆棧 (Call Stack)”的數據結構來管理所有的數據和操作。當一個函數被調用時,PVM 會為這個函數創建一個新的幀 (Frame),并將其推入調用堆棧的頂部。

一個幀包含了執行該函數所需的所有信息,主要包括:

  • 本地變量 (Local variables): 存儲函數內部定義的變量,如?ab?和?result

  • 操作數堆棧 (Operand Stack): 也叫評估堆棧 (Evaluation Stack),用于執行字節碼指令的臨時工作區。例如?BINARY_ADD?就是在這個堆棧上完成計算的。

  • 指令指針 (Instruction Pointer): 指向當前正在執行的字節碼指令。

  • 返回地址: 指向調用該函數的代碼位置,以便函數執行完畢后可以返回。

CPython解釋器核心源碼

https://github.com/python/cpython/blob/3.9/Python/ceval.c

? ? ? ?case TARGET(LOAD_FAST): {
? ? ? ? ? ? PyObject *value = GETLOCAL(oparg);
? ? ? ? ? ? if (value == NULL) {
? ? ? ? ? ? ? ? format_exc_check_arg(tstate, PyExc_UnboundLocalError,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?UNBOUNDLOCAL_ERROR_MSG,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?PyTuple_GetItem(co->co_varnames, oparg));
? ? ? ? ? ? ? ? goto error;
? ? ? ? ? ? }
? ? ? ? ? ? Py_INCREF(value);
? ? ? ? ? ? PUSH(value);
? ? ? ? ? ? FAST_DISPATCH();
? ? ? ? }

? ? ? ? case TARGET(LOAD_CONST): {
? ? ? ? ? ? PREDICTED(LOAD_CONST);
? ? ? ? ? ? PyObject *value = GETITEM(consts, oparg);
? ? ? ? ? ? Py_INCREF(value);
? ? ? ? ? ? PUSH(value);
? ? ? ? ? ? FAST_DISPATCH();
? ? ? ? }

? ? ? ? case TARGET(STORE_FAST): {
? ? ? ? ? ? PREDICTED(STORE_FAST);
? ? ? ? ? ? PyObject *value = POP();
? ? ? ? ? ? SETLOCAL(oparg, value);
? ? ? ? ? ? FAST_DISPATCH();
? ? ? ? }

留個坑

GIL、JIT、GC

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/94684.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/94684.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/94684.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MacOS Docker 安裝指南

MacOS Docker 安裝指南 引言 Docker 是一個開源的應用容器引擎,它允許開發者打包他們的應用以及應用的依賴包到一個可移植的容器中,然后發布到任何流行的 Linux 機器上,也可以實現虛擬化。容器是完全使用沙箱機制,相互之間不會有任何接口(類似 iPhone 的 app)。Docker …

Cisco 3750X交換機更新到IOS 15.2后無法啟動 提示:Boot process failed...

背景及故障現象 一臺新購入的二手Cisco 3750X-48P,原機自帶IOS軟件版本為12.x,可以正常工作。 但將IOS版本升級到15.2之后,在啟動過程中卡住。 第一次加載IOS軟件時是正常的,提示: Loading "flash:/c3750e-uni…

Redis Redis 常見數據類型

Redis 提供了 5 種數據結構,理解每種數據結構的特點對于 Redis 開發運維非常重要,同時掌握每種數據結構的常見命令,會在使用 Redis 的時候做到游刃有余。 一、預備知識 官方文檔:Commands | Docs (redis.io) 1、最核心的兩個命令…

金融風控實戰:Spring Boot + LightGBM 貸款預測模型服務化(超詳細版)

金融風控實戰:Spring Boot LightGBM 貸款預測模型服務化(超詳細版)一、整體架構設計二、模型訓練與優化1. 特征工程(Python)2. 模型評估與優化三、Spring Boot 服務實現1. 項目結構2. ONNX 模型服務3. 特征工程服務4.…

前端學習 7:EDA 工具

目錄 EDA 工具 Design Ware Synopsys CoreTools 套件 VCS verdi Design Compiler EDA 工具 常用的EDA工具主要來自三家公司:Synopsys、Cadence和Mentor(已被Siemens收購)。EDA,全稱電子設計自動化(Electronics …

windows有一個企業微信安裝包,腳本執行并安裝到d盤。

以下是將本地已有的企業微信安裝包安裝到D盤的完整PowerShell腳本&#xff0c;包含詳細的錯誤處理和進度反饋&#xff1a; <# .SYNOPSIS使用本地企業微信安裝包安裝到D盤 .DESCRIPTION自動檢測本地安裝包&#xff0c;靜默安裝到指定目錄支持.exe和.msi格式安裝包 #># 強制…

[LVGL] 布局系統 lv_flex, lv_grid | 輸入設備 lv_indev | union

第五章&#xff1a;布局系統&#xff08;lv_flex, lv_grid&#xff09; 歡迎回來&#xff01; 在第四章&#xff1a;樣式&#xff08;lv_style&#xff09;中&#xff0c;我們掌握了如何通過色彩、字體和圓角等特性美化部件。當界面元素具備視覺吸引力后&#xff0c;如何優雅…

Linux中的mkdir命令

基本語法mkdir 命令的基本語法如下&#xff1a;mkdir [選項] 目錄名創建單個目錄要創建一個新目錄&#xff0c;只需在 mkdir 后跟上目錄名稱。例如&#xff1a;mkdir new_folder這會在當前工作目錄下創建一個名為 new_folder 的目錄。創建多個目錄可以一次性創建多個目錄&#…

基于大數據的美食視頻播放數據可視化系統 Python+Django+Vue.js

本文項目編號 25003 &#xff0c;文末自助獲取源碼 \color{red}{25003&#xff0c;文末自助獲取源碼} 25003&#xff0c;文末自助獲取源碼 目錄 一、系統介紹二、系統錄屏三、啟動教程四、功能截圖五、文案資料5.1 選題背景5.2 國內外研究現狀 六、核心代碼6.1 查詢數據6.2 新…

微信小程序精品項目-基于springboot+Android的計算機精品課程學習系統(源碼+LW+部署文檔+全bao+遠程調試+代碼講解等)

博主介紹&#xff1a;??碼農一枚 &#xff0c;專注于大學生項目實戰開發、講解和畢業&#x1f6a2;文撰寫修改等。全棧領域優質創作者&#xff0c;博客之星、掘金/華為云/阿里云/InfoQ等平臺優質作者、專注于Java、小程序技術領域和畢業項目實戰 ??技術范圍&#xff1a;&am…

(五)系統可靠性設計

2024年博主考軟考高級系統架構師沒通過&#xff0c;于是決定集中精力認真學習系統架構的每一個環節&#xff0c;并在2025年軟考中取得了不錯的成績&#xff0c;雖然做信息安全的考架構師很難&#xff0c;但找對方法&#xff0c;問題就不大&#xff01; 本文主要是博主在學習過程…

Shuffle SOAR使用學習經驗

Shuffle SOAR 1. 基礎操作與配置1.1 環境搭建與系統要求1.1.1 硬件與操作系統要求Shuffle SOAR 平臺作為一款開源的安全編排、自動化與響應&#xff08;SOAR&#xff09;工具&#xff0c;其部署方式靈活&#xff0c;支持云端和自托管兩種模式。對于自托管部署&#xff0c;官方推…

騰訊云 EdgeOne 產品分析與免費套餐體驗指南

本文圍繞騰訊云 EdgeOne 展開&#xff0c;全方位介紹它的核心能力、免費套餐內容&#xff0c;以及如何快速上手、監控和排查常見問題&#xff0c;幫助個人開發者和中小企業在不產生額外成本的前提下體驗高性能的邊緣加速與安全防護。 一、產品概述 EdgeOne 定位 一體化云服務平…

npm ERR! Unsupported URL Type “workspace:“: workspace:./lib

如下 npm install npm ERR! code EUNSUPPORTEDPROTOCOL npm ERR! Unsupported URL Type "workspace:": workspace:./libnpm ERR! A complete log of this run can be found in: D:\IDEA\nodejs\node_cache\_logs\2025-08-06T08_21_32_592Z-debug-0.log原因及解決 pac…

微積分: 變化與累積

微積分,這門研究變化與累積的數學分支,其核心思想竟與東方哲學中"易"的概念不謀而合。《易經》有云:“易有太極,是生兩儀”,而微積分正是通過"微分"與"積分"這對辯證統一的操作,揭示了世間萬物變化與永恒的奧秘。 #mermaid-svg-UjO6qqMm0h…

web-vue工作流程

接續bmcweb流程。 當登錄openbmc web頁面后,瀏覽器會根據index.html中的js文件中的routes信息,自動獲取信息,比如當前的網絡設置信息、Datetime時區時間信息等。 以獲取網絡配置信息為例: 瀏覽器從app.js獲取到settins->network的route:”/settings/network”,加載對應…

全球化2.0 | 泰國IT服務商攜手云軸科技ZStack重塑云租賃新生態

在全球數字化轉型不斷加速的今天&#xff0c;泰國企業對于高質量云服務的需求日益旺盛。作為深耕本地市場逾二十年的行業領先IT服務商&#xff0c;泰國IT服務商不僅覆蓋了IT系統、軟件、硬件及網絡等多個領域&#xff0c;還持續引領當地技術服務創新。近期&#xff0c;該泰國IT…

一文搞懂Hive臨時表操作秘籍

Hive 臨時表&#xff1a;數據處理的得力助手 在大數據處理的廣闊領域中&#xff0c;Hive 憑借其強大的數據倉庫功能&#xff0c;成為了眾多數據分析師和開發者的得力工具。Hive 提供了類似 SQL 的查詢語言 HiveQL&#xff0c;讓我們能夠方便地對存儲在 Hadoop 分布式文件系統&a…

瞬態吸收光譜儀的基本原理

目錄 1. 基態與激發態 2. 時間上的動力學信息 3. pump-probe探測技術 4. 時間延遲和同一光源 5. 延時線和OPA 6. 差分信號 7. 斬波器 原視頻鏈接&#xff1a;瞬態吸收光譜儀的基本原理_嗶哩嗶哩_bilibili 1. 基態與激發態 當光照射在物質上時&#xff0c;組成物質的微觀…

迭代器與生成器:Python 中的高效數據遍歷機制

一、迭代器和生成器的基本概念 1. 迭代器的定義和工作原理 &#xff08;1&#xff09;迭代器的概念 迭代器&#xff08;Iterator&#xff09; 是 Python 中一種支持逐個訪問元素的對象&#xff0c;它遵循 迭代器協議&#xff08;Iterator Protocol&#xff09;&#xff0c;即實…