本文 AI 產出,尚未審核

Python 模組與套件(Modules & Packages)

主題:__name__ == '__main__'


簡介

在 Python 中,每一個檔案都是一個模組(module),而模組之間的關係常常透過 import 來建立。當我們直接執行一個 Python 檔案時,Python 會自動為該檔案設定一個特殊變數 __name__,其值為字串 '__main__'。如果該檔案是被其他模組匯入的,__name__ 則會是檔案的模組名稱(不含 .py 副檔名)。

利用 if __name__ == '__main__': 這段判斷式,我們可以**區分「直接執行」與「被匯入」**兩種情況,從而讓程式碼在不同情境下有不同的行為。這是撰寫可重用、可測試、易維護的 Python 程式的基礎技巧,也是每位 Python 開發者必須熟悉的概念。


核心概念

1. __name 變數的來源

  • 直接執行python myscript.pymyscript.py__name__'__main__'
  • 被匯入import myscript → 在 myscript.py 中,__name__'myscript'(模組名稱)。

小提醒__name__ 是 Python 內建的全域變數,不需要自行宣告,只要在程式中直接使用即可。

2. 為什麼要使用 if __name__ == '__main__':

情境 常見需求 使用 if __name__ == '__main__': 的好處
腳本測試 想快速驗證模組功能 測試程式碼只在直接執行時跑,匯入時不會干擾
範例說明 撰寫教學或範例程式 讀者可以直接執行範例,同時模組仍能被其他程式使用
CLI 工具 建立命令列介面 只在作為主程式時解析參數,作為函式庫時不會觸發 CLI

3. 基本範例

# hello.py
def greet(name: str) -> str:
    """回傳問候語"""
    return f"Hello, {name}!"

# 只有在直接執行此檔案時才會跑下面的程式
if __name__ == '__main__':
    # 這裡可以放測試或示範程式
    print(greet("World"))

執行 python hello.py 會印出 Hello, World!;而如果在其他檔案中 import hello,則不會自動執行 print,只會提供 greet 函式供使用。

4. 多個模組互相引用的情況

# a.py
def func_a():
    print("Function A")

if __name__ == '__main__':
    from b import func_b
    func_b()          # 只在直接執行 a.py 時呼叫 b 的功能
# b.py
def func_b():
    print("Function B")

if __name__ == '__main__':
    from a import func_a
    func_a()          # 只在直接執行 b.py 時呼叫 a 的功能

說明:兩個檔案互相 import,但因為 if __name__ == '__main__' 的保護,不會產生循環 import,除非同時直接執行兩個檔案。

5. 結合 argparse 建立 CLI

# calc.py
import argparse

def add(a: int, b: int) -> int:
    """回傳兩數相加的結果"""
    return a + b

def sub(a: int, b: int) -> int:
    """回傳兩數相減的結果"""
    return a - b

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="簡易計算機")
    parser.add_argument('op', choices=['add', 'sub'], help='操作類型')
    parser.add_argument('x', type=int, help='第一個數字')
    parser.add_argument('y', type=int, help='第二個數字')
    args = parser.parse_args()

    if args.op == 'add':
        print(add(args.x, args.y))
    else:
        print(sub(args.x, args.y))

使用方式

$ python calc.py add 3 5
8
$ python calc.py sub 10 4
6

calc.py 被其他模組匯入時,addsub 仍可直接呼叫,而不會執行 argparse 的解析程式。


常見陷阱與最佳實踐

陷阱 說明 解決方式
忘記加 if __name__ == '__main__': 測試程式碼會在匯入時自動執行,造成副作用(例如寫檔、印出訊息) 務必 把測試或示範程式碼包在此判斷式內
在判斷式內使用相對匯入 (from .module import ...) 直接執行時相對匯入會失敗(因為沒有包的上下文) 改用絕對匯入或在 if 內改寫為 import 的方式
把大量程式碼寫在 if 區塊內 會讓模組的可讀性下降,且不易單元測試 將主要邏輯抽成函式,在 if 中只呼叫這些函式
if 區塊內直接呼叫 sys.exit() 匯入模組時會意外終止程式 使用 argparse 或自行檢查參數,避免在匯入時觸發退出

最佳實踐

  1. 保持 if __name__ == '__main__': 只負責「入口」
    • 把功能實作放在函式或類別中,讓其他模組可以直接使用。
  2. 使用 main() 函式
    def main() -> None:
        # 解析參數、呼叫主要功能
        ...
    
    if __name__ == '__main__':
        main()
    
    這樣做的好處是 main() 可以被單元測試框架直接呼叫。
  3. 加入型別註解與 docstring
    • 讓 IDE、型別檢查工具(mypy)以及使用者都能快速瞭解函式的行為。
  4. 避免在模組層級執行 I/O
    • 讀寫檔案、網路請求等操作應該放在 main() 或其他函式內,以免匯入時產生不必要的 I/O。

實際應用場景

場景 為什麼需要 __name__ == '__main__' 範例簡述
資料前處理腳本 團隊成員會在不同步驟匯入共用的資料清理函式 preprocess.py 定義 clean(df);在 if 區塊內寫入 if __name__ == '__main__': 讀取 CSV、呼叫 clean、輸出結果
機器學習模型訓練 同一套程式同時提供「訓練」與「推論」兩種功能 train.py 包含 train_model()predict()if 區塊決定是跑訓練流程還是僅載入模型
CLI 工具套件 需要提供命令列介面,同時讓套件能作為函式庫被其他程式使用 如前面的 calc.py,使用 argparse 只在直接執行時解析參數
自動化測試 測試檔案會匯入被測試的模組,避免測試程式碼干擾測試環境 test_util.py 只在 if 區塊內執行手動測試,正式測試時由 pytest 呼叫模組內函式
多層套件入口 大型專案可能有 __init__.py 作為套件入口,仍需要執行特定腳本 mypackage/__main__.pypython -m mypackage 成為可執行指令,內部仍使用 if __name__ == '__main__': 來啟動主流程

總結

  • __name__ 是 Python 為每個模組自動設定的全域變數,直接執行時為 '__main__',匯入時為模組名稱
  • 使用 if __name__ == '__main__': 可以將執行入口與函式庫功能分離,讓程式碼在不同情境下表現一致且安全。
  • 最佳實踐:把主要邏輯寫成 main() 或其他可重用的函式,僅在 if 區塊內呼叫;避免在模組層級執行 I/O 或產生副作用。
  • 這個技巧在 腳本測試、CLI 工具、資料前處理、機器學習工作流 等多種實務場景中都相當重要,是撰寫乾淨、可維護 Python 專案的基礎。

掌握 __name__ == '__main__' 的用法,你就能在寫模組時同時兼顧 可直接執行的便利作為套件被重用的彈性,讓你的 Python 程式更專業、更可靠。祝你寫程式愉快!