traverse 這樣的遞迴 (Recursion) 結構,
正是用來處理「樹狀結構 (Tree Structure)」的最強大武器。
不管是 JSON 設定檔、資料夾裡的檔案架構,
還是程式語言的 AST (抽象語法樹, Abstract Syntax Tree),
在本質上都是一種樹狀結構:它們都有一個「根節點」,
然後往下分岔出無數個「子節點」,子節點又會繼續往下分岔。
這裡我們用一個簡單的例子,
帶你了解如何用遞迴來「走訪 (Traverse)」這些資料,
並且看看這跟 AST 有什麼關聯。
第一步:理解遞迴的核心精神 —— 「俄羅斯娃娃」
遞迴的核心精神就是「自己呼叫自己」。
想像你在玩一個巨大的俄羅斯娃娃,
你的目標是把裡面所有的玩偶都拿出來排好。
你可以把這件事寫成一個規則:
def 打開娃娃(當前的娃娃):
# 1. 終止條件 (Base Case):如果沒有更小的了,就停下來
if 當前的娃娃.是實心的():
紀錄("找到最核心的實心娃娃了!")
return
# 2. 遞迴條件 (Recursive Step):如果裡面還有娃娃
else:
# 先從當前的娃娃肚子裡,把小娃娃「拿出來」
裡面的小娃娃 = 當前的娃娃.取出內容物()
# 然後針對這個小娃娃,再次執行「打開娃娃」的任務(自己呼叫自己)
打開娃娃(裡面的小娃娃) 第二步:簡化的 JSON 遞迴範例
回到你程式碼裡的 traverse 結構。
假設我們有一個非常簡單的 JSON
(其實在 Python 裡這就是巢狀字典和列表),
我們目標是「找出所有存在 class_name 的積木」。
data = {
"step1": {
"class_name": "LoginStep",
"description": "User logs in"
},
"step2": [
{
"class_name": "VerificationStep",
"type": "email"
},
"這是一個普通的字串,不用理他"
]
}這個結構裡有字典 (dict) 也有列表 (list)。我們的遞迴可以這樣寫:
def simple_traverse(node):
# 只要是字典,我們就來檢查
if isinstance(node, dict):
# 1. 如果字典裡有 class_name,我們就抓到目標了!印出來
if "class_name" in node:
print(f"找到目標囉!class_name 叫做: {node['class_name']}")
# 2. 為了確保我們沒有漏掉字典「更深處」的東西,我們把字典裡所有的 value 都拿出來,再走訪一次
for key, value in node.items():
simple_traverse(value) # 遞迴:繼續往下層找
# 如果遇到的是一個列表,那就把列表裡的東西一個一個拿出來走訪
elif isinstance(node, list):
for item in node:
simple_traverse(item) # 遞迴:繼續往下層找
# 開始走訪
simple_traverse(data)輸出結果:
你看,我們完全不需要知道這個 JSON 到底有幾層,
只要透過 simple_traverse(value) 和 simple_traverse(item),
程式就會像藤蔓一樣自動延伸到底部,這就是遞迴的魅力!
第三步:這跟 AST (抽象語法樹) 的關係?
是的,處理 AST 百分之百就是用這種遞迴結構!
當 Python (或其他編譯器) 讀取你的程式碼時,
它會把文字轉換成一棵抽象語法樹 (AST)。
例如你寫了一句 a = 1 + 2,在 AST 裡面可能長這樣 (簡化版):
ast_tree = {
"type": "Assign", # 這是一個指派運算 (Assign)
"targets": ["a"], # 左邊是要被賦值的變數 "a"
"value": { # 右邊要賦的值是一個加法運算 (BinOp)
"type": "BinOp",
"operator": "Add", # 運算子是加號
"left": { "type": "Constant", "value": 1 }, # 左邊的常數 1
"right": { "type": "Constant", "value": 2 } # 右邊的常數 2
}
}這不就跟前面的 JSON 長得一模一樣嗎!
如果開發者想用 AST 工具來檢查這份程式碼「有沒有用到加法」,他們寫的程式碼邏輯,跟你寫在 get_path_for_class_v4.py 裡的 traverse 幾乎是同一個模子刻出來的:
def check_addition_ast(node):
# 如果這是一個字典型態的節點
if isinstance(node, dict):
# 如果這個節點是一個 加法運算 (Add)
if node.get("operator") == "Add":
print("警告:這段程式碼使用了加法!")
# 繼續遞迴走訪底下的所有節點 (把左邊、右邊都拆出來檢查)
for key, child_node in node.items():
check_addition_ast(child_node)
check_addition_ast(ast_tree)
# 輸出: 警告:這段程式碼使用了加法!總結
遞迴 (Recursion) 最適合用來處理「深度未知的主體」,
例如 JSON、資料夾、XML、或是 AST。
get_path_for_class_v4.py 的寫法:
檢查是否為 dict/list -> 做該層該做的事 -> 取出子結點繼續 traverse,
這個骨架其實是所有語法解析器、
甚至爬蟲都在使用的標準設計模式 (Design Pattern)。
等你以後要處理像爬梳 HTML 標籤或是 AST 時,
這個老朋友一定會再跟你見面的!
推薦hahow線上學習python: https://igrape.net/30afN