Python 註解教學:# 單行註解與 Docstring 文件字串

Python 的註解(Comment)是寫給人看的說明文字,讓程式碼更易讀、更易維護。本文完整介紹兩種核心用法:# 單行註解的快速標注技巧,以及 """ 三引號 Docstring 文件字串的標準格式;並說明兩者在量化交易開發中的最佳實踐,包含函數說明、策略邏輯標注,以及如何透過 help() 在執行期讀取 Docstring,幫助你從第一行程式碼就養成專業的文件習慣。

Cinematic dark alchemy forge with ancient stone tablets inscribed with glowing rune annotations and margin notes, amber firelight illuminating a scholar's open grimoire covered in handwritten comments beside glowing code crystals, deep dramatic shadows, scattered ink quills and scrolls, cinematic lighting, 16:9

什麼是 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 格式之一,清楚分隔 ArgsReturnsRaises 等區段,搭配 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 則是函數的使用說明書。兩者相輔相成,缺一不可。

張貼留言

較新的 較舊