本文 AI 產出,尚未審核

Python – 變數與資料型別(Variables & Data Types)

主題:None 型別


簡介

在 Python 中,None 是唯一的 空值(null) 表示,它不等同於 0、空字串 '' 或空容器 []。許多程式在設計 API、資料庫存取或函式回傳時,都需要用到「無值」的概念。若對 None 的行為不夠了解,常會出現難以偵測的錯誤或不一致的程式邏輯。

本篇文章將從 概念、實作範例、常見陷阱與最佳實踐 三個面向,深入說明 None 型別的使用方式,並提供 實務場景 的範例,協助讀者在日常開發中正確、有效地處理「無值」情況。


核心概念

1. None 是什麼?

  • None 是 Python 內建的 單例(singleton),代表「沒有任何值」或「值未知」。
  • 它的類型是 NoneType,且全域只有一個實例:None 本身。
>>> type(None)
<class 'NoneType'>
>>> None is None
True

2. 為什麼要使用 None

  • 區別於 falsy 值0''[]{} 都會在布林運算中被視為 False,但它們仍然是具體的值;None 則明確表示「不存在」。
  • 函式回傳:當函式執行成功但沒有具體結果時,可回傳 None,讓呼叫端知道「沒有資料」而不是「錯誤」。
  • 預設參數:在可選參數的情況下,使用 None 作為預設值,方便在函式內部判斷是否被呼叫者提供。

3. 判斷 None 的正確方式

在 Python 中,使用 isis not 來比較 None,而不是 ==is 檢查的是物件身分,保證不會因為自訂類別覆寫 __eq__ 而產生意外結果。

def check(value):
    if value is None:          # ✅ 正確
        print("沒有提供值")
    else:
        print(f"值為 {value}")

check(None)      # 輸出: 沒有提供值
check(0)         # 輸出: 值為 0

4. None 在型別提示(type hint)中的使用

Python 3.10 以後,可直接用 None 表示 單一 None,若要表示「可能是 None」或其他型別」,則使用 typing.Optional| None`:

from typing import Optional

def greet(name: Optional[str] = None) -> str:
    if name is None:
        return "Hello, Guest!"
    return f"Hello, {name}!"

或是 Python 3.10+ 的寫法:

def greet(name: str | None = None) -> str:
    ...

5. None 作為哨兵(sentinel)

有時候需要一個「特殊」的佔位值來區分「未提供」與「提供了 falsy 值」的情況,None 本身就是最常見的哨兵。例如:

def fetch(limit: int | None = None):
    # 若 limit 為 None,表示不限制筆數
    if limit is None:
        limit = 1000  # 預設上限
    # 之後的程式會使用 limit 變數
    ...

程式碼範例

以下提供 5 個實用範例,涵蓋 None 的不同使用情境,並在每段程式碼中加入說明註解。

範例 1:函式回傳 None 表示「找不到」

def find_user(username: str) -> dict | None:
    """在模擬的使用者資料庫中搜尋使用者,找不到時回傳 None。"""
    fake_db = {
        "alice": {"id": 1, "email": "alice@example.com"},
        "bob":   {"id": 2, "email": "bob@example.com"},
    }
    return fake_db.get(username)          # 若不存在,dict.get 會回傳 None

user = find_user("charlie")
if user is None:                         # 使用 is 判斷
    print("找不到使用者")
else:
    print(f"使用者資料: {user}")

範例 2:預設參數使用 None 防止可變物件共享

def append_item(item, lst: list | None = None):
    """若未提供 lst,建立新的列表;避免預設值是 [] 的可變共享問題。"""
    if lst is None:                      # 判斷是否使用預設值
        lst = []                         # 每次呼叫都會產生新列表
    lst.append(item)
    return lst

print(append_item(1))        # [1]
print(append_item(2))        # [2]  <-- 不會與上一次的結果混合

範例 3:使用 None 作為 API 回傳的缺失欄位

import json

response = '{"id": 10, "name": "Widget", "price": null}'
data = json.loads(response)               # JSON 的 null 會被轉成 Python 的 None

price = data["price"]
if price is None:
    print("價格尚未設定")
else:
    print(f"商品價格: {price}")

範例 4:None 與布林值的差異

def truthy_demo(value):
    if value:
        print("此值在布林環境中為 True")
    else:
        print("此值在布林環境中為 False")

truthy_demo(0)        # False
truthy_demo("")       # False
truthy_demo([])       # False
truthy_demo(None)     # False   <-- 與 0、''、[] 同樣為 False,但意義不同

範例 5:typing.OptionalNone 結合的型別提示

from typing import Optional

def divide(a: float, b: Optional[float] = None) -> float:
    """
    若 b 為 None,使用預設的除數 1,避免 ZeroDivisionError。
    """
    divisor = b if b is not None else 1.0
    return a / divisor

print(divide(10, 2))   # 5.0
print(divide(10))      # 10.0   <-- b 為 None 時使用預設 1.0

常見陷阱與最佳實踐

陷阱 說明 建議的解決方式
使用 == 比較 None 可能因自訂類別覆寫 __eq__ 而產生不預期結果。 永遠使用 is / is not 來檢測 None
預設參數使用可變物件(如 []{} 多次呼叫函式會共享同一個實例,導致資料污染。 None 為預設值,於函式內部建立新物件(如範例 2)。
None 視為 False 直接使用 雖然 None 在布林運算中為 False,但有時需要區分「未提供」與「提供了 falsy 值」的差別。 針對 None 使用 顯式的 is None 判斷,而非僅靠 if not value:
忘記在函式回傳前加入 return None 若函式在某條件下未執行 return,Python 會自動回傳 None,可能讓呼叫端誤以為執行成功。 明確寫出 return None,或在文件說明回傳可能為 None
None 放入資料結構後忘記檢查 迭代或計算時若未排除 None,會拋出 TypeError(如 None + 1)。 在使用前 過濾 None 或使用 if item is not None: 進行保護。

最佳實踐要點

  1. 使用 is / is not 判斷 None:確保檢查的是物件身分。
  2. 預設參數採 None + 內部建立:避免可變預設值的共享問題。
  3. 型別提示搭配 Optional:提升 IDE 識別與靜態分析的正確性。
  4. 在文件或 docstring 中說明可能回傳 None:讓使用者了解 API 行為。
  5. 在資料清理階段主動移除或替換 None:防止後續運算出錯。

實際應用場景

  1. 資料庫查詢結果可能為空
    • 使用 None 代表欄位為 NULL,或查詢不到任何列。
  2. Web API 回傳的可選欄位
    • JSON 中缺失的欄位會被解析為 None,程式可依此決定是否提供預設值或拋出錯誤。
  3. 函式的可選參數
    • 例如 datetime.replace(tzinfo=None)tzinfo 若為 None 表示「去除時區資訊」。
  4. 演算法中的哨兵值
    • 在搜尋或排序過程中,用 None 代表「尚未分配」或「尚未計算」。
  5. 測試與 Mock
    • 在單元測試時,透過傳入 None 模擬「缺失」或「未實作」的情境,驗證程式的容錯能力。

總結

None 雖然只是一個簡單的 單例,卻是 Python 中處理「無值」概念的核心工具。正確使用 None 能讓程式:

  • 明確區分「沒有值」與「值為 falsy」的情況。
  • 防止可變預設參數帶來的隱蔽錯誤。
  • 透過型別提示提升代碼可讀性與安全性。

在開發過程中,養成使用 is 判斷 None、以 None 作為預設哨兵、並在文件中說明可能回傳 None 的好習慣,將大幅降低因「空值」導致的 Bug。希望本篇文章能幫助你在 Python 專案中更自信地掌控 None,寫出更健全、易維護的程式碼。祝你開發順利!