Python 的註解(Comment)是寫給人看的說明文字,讓程式碼更易讀、更易維護。本文完整介紹兩種核心用法:# 單行註解的快速標注技巧,以及 """ 三引號 Docstring 文件字串的標準格式;並說明兩者在量化交易開發中的最佳實踐,包含函數說明、策略邏輯標注,以及如何透過 help() 在執行期讀取 Docstring,幫助你從第一行程式碼就養成專業的文件習慣。
什麼是 Python 註解?
Python 的註解是程式碼中不會被執行的說明文字,用來解釋程式邏輯、標記待辦事項,或暫時停用某段程式碼。Python 提供兩種主要的註解形式:用 # 開頭的單行註解,以及用三引號 """ 或 ''' 包覆的多行 Docstring。
Docstring(Document String,文件字串)是一種特殊的字串,放在函數、類別或模組定義的第一行,Python 直譯器會將它儲存在物件的 __doc__ 屬性中,讓你之後可以透過 help() 動態查詢說明——就像煉金坊配方書上的開頭摘要,讓後人一翻就知道這爐藥水的效果與用法。
核心語法:# 與 Docstring
# === 單行註解:# 後面加空格再寫說明(PEP 8 建議)===
# 這是一個計算移動平均的函數
def moving_average(prices, window=5):
return sum(prices[-window:]) / window
result = moving_average([100, 102, 105, 103, 108]) # 取最近 5 日均價
print(result) # 輸出:103.6
# 暫時停用某行(除錯時常用)
# print("這行被停用,不會執行")
PEP 8 風格規範:#與說明文字之間留一個空格,即# 說明,而非#說明。行尾註解(Inline comment)與程式碼之間至少留兩個空格,可大幅提升可讀性。
# === Docstring:放在函數/類別定義的第一行 ===
def calculate_rsi(prices, period=14):
"""
計算相對強弱指數(RSI)。
Args:
prices (list): 收盤價序列,至少需要 period+1 筆資料。
period (int): 計算週期,預設 14 日。
Returns:
float: RSI 數值,介於 0 到 100 之間。
Example:
>>> calculate_rsi([44, 45, 46, 45, 47, 48, 46, 47], period=7)
71.43
"""
# 計算漲跌幅
changes = [prices[i] - prices[i-1] for i in range(1, len(prices))]
gains = [c for c in changes if c > 0]
losses = [-c for c in changes if c < 0]
avg_gain = sum(gains[-period:]) / period if gains else 0
avg_loss = sum(losses[-period:]) / period if losses else 0.001 # 避免除以零
rs = avg_gain / avg_loss
return 100 - (100 / (1 + rs))
# 執行期查詢 Docstring
help(calculate_rsi) # 印出完整說明
print(calculate_rsi.__doc__) # 直接讀取 __doc__ 屬性
Docstring 用三引號"""包覆,可以跨多行撰寫,且不需要賦值給任何變數。Python 直譯器遇到函數定義後的第一個字串字面值,就會自動將它存入__doc__屬性,這是它與普通多行字串最根本的差異。
# === 單行 Docstring:說明簡短時可用單行 ===
def pip_value(lot_size, price_change):
"""計算單口合約的點值損益(美元)。"""
return lot_size * price_change * 100000
# === 模組層級 Docstring:放在整個 .py 檔案最頂端 ===
"""
strategy_ma_cross.py
雙均線交叉策略:快線上穿慢線買入,快線下穿慢線賣出。
作者:離火煉金坊
版本:1.0.0
"""
規則與注意事項
| 項目 | 正確範例 | 錯誤範例 |
|---|---|---|
# 後加空格 |
# 計算均線 |
#計算均線(缺空格,PEP 8 不符) |
| Docstring 放在第一行 | 函數定義冒號後立刻寫 """... |
先寫一行程式碼再寫 """(不會被視為 Docstring) |
| Docstring 用三引號 | """說明""" 或 '''說明''' |
"說明"(單引號字串,無法跨行) |
| 行尾註解間距 | x = 1 # 初始值(兩個空格) |
x = 1 # 初始值(一個空格,PEP 8 不符) |
| 多行「假註解」 | 連續使用多個 # 單行 |
在函數外用 """... 當多行註解(會佔記憶體) |
常見錯誤與防呆
錯誤一:Docstring 沒放在第一行,導致 __doc__ 為 None
# ❌ 錯誤寫法:先寫程式碼再寫三引號字串,不會被視為 Docstring
def bad_func(x):
result = x * 2 # 這行先執行了
"""這不是 Docstring,只是一個沒用的字串常數。"""
return result
print(bad_func.__doc__) # 輸出:None
# ✅ 正確寫法:三引號緊接在冒號後的第一行
def good_func(x):
"""將輸入值乘以 2 並回傳。"""
result = x * 2
return result
print(good_func.__doc__) # 輸出:將輸入值乘以 2 並回傳。
錯誤二:用三引號當多行註解,在函數外濫用
# ❌ 容易誤解的寫法:函數外的三引號字串是字串物件,會佔記憶體
"""
這段文字看起來像多行註解
但其實是一個字串物件,Python 會建立它但不賦值給任何變數
在迴圈內大量使用會造成效能問題
"""
# ✅ 正確做法:多行說明用多個 # 行
# 這段說明是真正的多行註解
# Python 直譯器完全忽略 # 開頭的內容
# 不佔任何記憶體,安全且語意清晰
錯誤三:過度或過少添加註解
# ❌ 過度:解釋顯而易見的事,增加雜訊
x = x + 1 # 把 x 加上 1 ← 完全多餘
# ❌ 過少:關鍵邏輯毫無說明
rs = avg_gain / (avg_loss + 1e-10)
# ✅ 正確:解釋「為什麼」而非「是什麼」
rs = avg_gain / (avg_loss + 1e-10) # 加極小值 1e-10 防止 avg_loss=0 時除以零
進階用法
Google Style Docstring:量化開發推薦格式
Google Style 是目前最廣泛使用的 Docstring 格式之一,清楚分隔 Args、Returns、Raises 等區段,搭配 PyCharm、VS Code 可自動渲染為說明面板,非常適合多人協作的量化策略專案。
def backtest(strategy_func, ohlc_data, initial_capital=100000):
"""
執行策略回測並回傳績效報告。
Args:
strategy_func (callable): 策略函數,接受 OHLC DataFrame 並回傳訊號序列。
ohlc_data (list): OHLC 資料,每筆為 [date, open, high, low, close]。
initial_capital (float): 初始資金,預設 100,000 美元。
Returns:
dict: 包含以下鍵值的績效字典:
- 'total_return' (float): 總報酬率(%)
- 'max_drawdown' (float): 最大回撤(%)
- 'win_rate' (float): 勝率(%)
- 'trade_count' (int): 總交易次數
Raises:
ValueError: 若 ohlc_data 為空串列或 initial_capital <= 0。
Example:
>>> result = backtest(my_strategy, data, initial_capital=50000)
>>> print(result['total_return'])
12.5
"""
if not ohlc_data:
raise ValueError("ohlc_data 不能為空")
if initial_capital <= 0:
raise ValueError("initial_capital 必須大於 0")
# 實際回測邏輯(示意)
return {
'total_return': 12.5,
'max_drawdown': -8.3,
'win_rate': 55.0,
'trade_count': 42
}
TODO 與 NOTE 標籤:團隊協作慣例
在量化開發中,常以特定標籤在註解中標記待辦或提醒事項,主流 IDE(PyCharm、VS Code)都能識別這些標籤並高亮顯示,讓未完成的工作一目了然。
def calculate_sharpe(returns, risk_free_rate=0.02):
"""計算年化夏普比率。"""
# TODO: 改為支援日報酬輸入並自動年化
# FIXME: 當 returns 標準差為 0 時會拋出 ZeroDivisionError
# NOTE: risk_free_rate 預設使用美國 10 年期公債殖利率 2%
import statistics
avg_return = sum(returns) / len(returns)
std_return = statistics.stdev(returns)
# 夏普比率 = (平均報酬 - 無風險利率) / 報酬標準差
sharpe = (avg_return - risk_free_rate) / std_return
return round(sharpe, 4)
用 help() 動態查詢 Docstring
Python 的 help() 函數可在執行期讀取任何物件的 Docstring,在互動式開發(Jupyter Notebook、IPython)中查詢自訂函數的用法時非常實用,不需要翻閱原始碼。
# 查詢自訂函數的 Docstring
help(calculate_rsi)
# 直接讀取 __doc__ 屬性(適合程式化存取)
print(calculate_rsi.__doc__)
# 查詢 Python 內建函數的說明
help(len)
help(sorted)
# 在 Jupyter Notebook 中,可用 ? 快速查詢
# calculate_rsi? ← 只在 Jupyter 環境有效
煉金坊小叮嚀
許多初學者覺得「先把功能做出來,之後再補說明」,但實際上之後很少真的回頭補——包括我自己也犯過這個錯。我現在的習慣是:每寫一個函數,冒號後的第一件事就是寫 Docstring,哪怕只有一行也好。在量化開發中,一個策略函數可能三個月後才會被再次審視,那時連自己都忘記當初為什麼這樣設計。好的 Docstring 不是在解釋「這行程式碼做了什麼」,而是解釋「為什麼這樣設計、有哪些限制、怎麼正確使用」——這才是讓未來的你少罵現在的你的關鍵。單行 # 用來標注邏輯細節、邊界條件與待辦事項;Docstring 則是函數的使用說明書。兩者相輔相成,缺一不可。