本文 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 中,使用 is 或 is 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.Optional 與 None 結合的型別提示
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: 進行保護。 |
最佳實踐要點
- 使用
is/is not判斷None:確保檢查的是物件身分。 - 預設參數採
None+ 內部建立:避免可變預設值的共享問題。 - 型別提示搭配
Optional:提升 IDE 識別與靜態分析的正確性。 - 在文件或 docstring 中說明可能回傳
None:讓使用者了解 API 行為。 - 在資料清理階段主動移除或替換
None:防止後續運算出錯。
實際應用場景
- 資料庫查詢結果可能為空
- 使用
None代表欄位為NULL,或查詢不到任何列。
- 使用
- Web API 回傳的可選欄位
- JSON 中缺失的欄位會被解析為
None,程式可依此決定是否提供預設值或拋出錯誤。
- JSON 中缺失的欄位會被解析為
- 函式的可選參數
- 例如
datetime.replace(tzinfo=None),tzinfo若為None表示「去除時區資訊」。
- 例如
- 演算法中的哨兵值
- 在搜尋或排序過程中,用
None代表「尚未分配」或「尚未計算」。
- 在搜尋或排序過程中,用
- 測試與 Mock
- 在單元測試時,透過傳入
None模擬「缺失」或「未實作」的情境,驗證程式的容錯能力。
- 在單元測試時,透過傳入
總結
None 雖然只是一個簡單的 單例,卻是 Python 中處理「無值」概念的核心工具。正確使用 None 能讓程式:
- 明確區分「沒有值」與「值為 falsy」的情況。
- 防止可變預設參數帶來的隱蔽錯誤。
- 透過型別提示提升代碼可讀性與安全性。
在開發過程中,養成使用 is 判斷 None、以 None 作為預設哨兵、並在文件中說明可能回傳 None 的好習慣,將大幅降低因「空值」導致的 Bug。希望本篇文章能幫助你在 Python 專案中更自信地掌控 None,寫出更健全、易維護的程式碼。祝你開發順利!