Python 最與眾不同的設計之一,就是用縮排(Indentation)取代大括號 {} 來定義程式碼區塊。這不是風格選擇,而是語法規則——縮排錯誤會直接讓程式無法執行。本文完整說明 Python 縮排的運作原理、4 個空格的 PEP 8 標準規範、巢狀縮排的層次結構,以及空格與 Tab 混用的危險,並以 if、for、def 三種最常見情境示範正確用法,幫助你從第一行程式碼就建立正確習慣。
什麼是 Python 縮排?
在 C、Java、JavaScript 等語言中,程式碼區塊用大括號 {} 包圍;但在 Python 中,縮排(向右移動的空白)就是區塊的邊界。冒號 : 開啟一個新區塊,接下來所有縮排相同層次的程式碼,都屬於這個區塊;一旦縮排退回,就代表區塊結束。這個設計讓 Python 程式碼在視覺上天然具有清晰的層次結構,也是 Python 「可讀性至上」哲學的體現。
可以把縮排想成煉金坊的巢狀熔爐:外層爐膛是函數,內層爐膛是 if 判斷,最裡層才是真正執行的反應。每往內一層,就代表「在前一個條件成立後,才執行這段操作」。縮排決定的不只是排版,而是程式的執行邏輯。
核心語法:縮排與程式碼區塊
# === 基本規則:冒號後的下一行必須縮排 ===
# if 條件判斷:符合條件時執行縮排內的程式碼
price = 185.5
if price > 180:
print("價格高於 180") # 縮排 4 個空格,屬於 if 區塊
print("達到進場條件") # 同樣縮排 4 格,仍在 if 區塊內
print("這行沒有縮排") # 退回原層,不屬於 if,一定會執行
# for 迴圈:每次迭代都執行縮排內的程式碼
closes = [183.5, 185.2, 187.8, 183.5, 188.1]
for c in closes:
result = c * 1.02 # 縮排,在 for 區塊內
print(result) # 縮排,在 for 區塊內
print("迴圈結束") # 無縮排,for 結束後才執行
# def 函數定義:函數主體必須縮排
def greet(name):
message = f"Hello, {name}!" # 縮排,函數主體
return message # 縮排,函數主體
result = greet("離火煉金坊")
print(result) # 無縮排,函數外部
Python 官方風格指南 PEP 8 規定:每個縮排層次使用 4 個空格(不是 1 個、2 個,也不是 Tab)。幾乎所有主流 IDE(VS Code、PyCharm)預設都會在你按下 Tab 鍵時自動插入 4 個空格,讓你無需手動敲 4 次空白鍵。
# === 巢狀縮排:多層區塊逐層往右 ===
def check_signal(price, rsi):
"""根據價格與 RSI 判斷交易訊號(巢狀縮排示範)。"""
if price > 180: # 第一層縮排(4 格):在函數內
if rsi < 30: # 第二層縮排(8 格):在外層 if 內
print("超賣 + 高價:強烈買入訊號")
return "BUY" # 第二層縮排,仍在內層 if
elif rsi > 70: # 第二層縮排,elif 與內層 if 對齊
print("超買 + 高價:觀望")
return "HOLD"
else: # 第二層縮排,else 與內層 if 對齊
print("RSI 中性,持倉")
return "HOLD"
else: # 第一層縮排,與外層 if 對齊
print("價格低於門檻,不進場")
return "SKIP"
# 呼叫函數(不縮排,在函數外)
signal = check_signal(185.5, 28)
print(f"訊號:{signal}")
巢狀縮排的關鍵是:同一層次的if、elif、else必須對齊(起始空格數完全相同),它們的程式碼主體再各自多縮排 4 格。層次愈深,程式碼愈往右移動。
規則與注意事項
| 規則 | 正確範例 | 錯誤範例 |
|---|---|---|
| 縮排量:4 個空格(PEP 8) | if x: |
if x:(2 格,不一致就報錯) |
| 冒號後必須縮排 | def f(): |
def f():(IndentationError) |
| 同層對齊 | if a: |
if a:(else 縮排錯誤) |
| 不可混用 Tab 與空格 | 統一使用空格(IDE 設定 Tab = 4 spaces) | 同一檔案 Tab + 空格混用(TabError) |
| 空白行不影響縮排 | 函數間可加空白行分隔(PEP 8 建議 2 行) | 在縮排區塊中間加入縮排不一致的行 |
常見錯誤與防呆
錯誤一:IndentationError — 縮排層次不一致
# ❌ 錯誤寫法(同一區塊縮排不一致)
# IndentationError: unexpected indent
def calc(x):
result = x * 2
return result # 多了 2 格空白,與上行不對齊
# ✅ 正確寫法:同一區塊所有行用相同縮排量
def calc(x):
result = x * 2
return result # 與上行同樣 4 格縮排
錯誤二:邏輯縮排錯誤 — print 放錯層次,輸出結果不符預期
# ❌ 邏輯錯誤:print 縮排在 for 內,每次迴圈都印一次
total = 0
closes = [183.5, 185.2, 187.8]
for c in closes:
total += c
print(f"目前總和:{total}") # 印了 3 次!
# ✅ 正確做法:只想看最終結果,print 縮排退回 for 外
total = 0
for c in closes:
total += c
print(f"最終總和:{total}") # 迴圈全部跑完才印一次
錯誤三:TabError — Tab 與空格混用
# ❌ 危險:某行用 Tab,其他行用空格,視覺上看起來對齊,執行卻報錯
# TabError: inconsistent use of tabs and spaces in indentation
def bad_func():
x = 1 # 這行用 Tab(→)縮排
return x # 這行用 4 個空格縮排,兩種混用!
# ✅ 解決方法:IDE 設定「Tab 自動轉換為 4 個空格」
# VS Code:settings → Editor: Tab Size = 4 + Insert Spaces = ✓
# PyCharm:Preferences → Code Style → Python → Tab size = 4 + Use tab character = ✗
def good_func():
x = 1 # 統一 4 個空格
return x # 統一 4 個空格
進階用法
pass 佔位符:保持縮排結構完整
Python 規定縮排區塊不能為空——函數、if、for 的主體至少要有一行程式碼。開發量化策略時,常需要先定義函數骨架但尚未填入邏輯,這時用 pass 作為佔位符,讓程式碼合法執行而不報錯。
# pass:空區塊佔位,讓結構合法
def calculate_drawdown(equity_curve):
pass # TODO: 之後實作最大回撤計算
def on_tick(price):
pass # TODO: 之後實作即時策略邏輯
class Strategy:
pass # TODO: 之後補充類別方法
# if-else 骨架先建立,細節後填
signal = "BUY"
if signal == "BUY":
pass # TODO: 執行買入邏輯
elif signal == "SELL":
pass # TODO: 執行賣出邏輯
else:
pass # HOLD 不動作
長串列與字典的縮排慣例
當函數參數、串列、字典內容很多時,PEP 8 建議將每個元素獨立一行並縮排對齊,讓程式碼清晰易讀,不需要水平捲動。
# 多行串列:每個元素獨立一行,最後一個元素後加逗號(trailing comma)
ohlc_data = [
[2024_01_02, 182.5, 185.9, 181.0, 185.2],
[2024_01_03, 185.5, 188.2, 184.7, 187.8],
[2024_01_04, 187.2, 188.5, 182.1, 183.5], # trailing comma:方便之後新增行
]
# 多行字典
strategy_params = {
"fast_period": 5,
"slow_period": 20,
"stop_loss_pct": 0.02,
"take_profit_pct": 0.05,
}
# 多行函數呼叫:參數對齊開括號,或統一縮排 4 格
result = calculate_rsi(
prices=closes,
period=14,
smooth=True,
)
量化策略中的多層縮排實戰
量化策略邏輯通常需要多層巢狀縮排:外層是迴圈遍歷 K 線,內層是條件判斷,最裡層是下單執行。清楚掌握縮排層次,才能確保策略邏輯按預期執行。
def run_backtest(ohlc_data, fast=5, slow=20):
"""
簡易雙均線回測策略。
外層:遍歷每根 K 線
中層:判斷是否有足夠資料
內層:判斷均線交叉方向
"""
position = None # 目前持倉狀態(None / 'LONG')
trades = [] # 記錄所有交易
for i in range(slow, len(ohlc_data)): # 第一層:遍歷 K 線
# 計算快慢均線
fast_ma = sum(c[4] for c in ohlc_data[i-fast:i]) / fast
slow_ma = sum(c[4] for c in ohlc_data[i-slow:i]) / slow
close = ohlc_data[i][4]
if position is None: # 第二層:未持倉時找進場
if fast_ma > slow_ma: # 第三層:快線上穿慢線
position = 'LONG'
trades.append({
'action': 'BUY',
'price': close,
'index': i,
})
print(f"買入:{close:.2f}")
elif position == 'LONG': # 第二層:持倉時找出場
if fast_ma < slow_ma: # 第三層:快線下穿慢線
position = None
trades.append({
'action': 'SELL',
'price': close,
'index': i,
})
print(f"賣出:{close:.2f}")
return trades # 退回函數第一層,回傳結果
煉金坊小叮嚀
縮排是 Python 的靈魂,但也是許多初學者第一個踩坑的地方。我自己剛開始時最常犯的錯誤,是把 return 或 print 縮排放錯層次,結果函數提早結束或迴圈每次都印出結果。我的建議是:從一開始就在 IDE 設定「Tab 自動轉為 4 個空格」,並開啟「顯示空白符號」功能,這樣縮排問題一眼就看得出來。在撰寫量化策略時,我習慣先用 pass 把整個函數骨架建好,確認縮排層次正確後,再逐一填入邏輯——這樣能避免因為層次錯亂導致策略在錯誤的時機點執行,難以排查的 bug。記住:縮排不只是排版,它決定了你的程式碼「在什麼條件下」、「執行幾次」,是量化邏輯正確性的基石。