Python 課程 – 運算子(Operators)
主題:成員運算子(in / not in)
簡介
在 Python 中,成員運算子(in 與 not in)是檢查「某個元素是否屬於」資料容器的利器。無論是字串、列表、元組、集合或字典,只要是可迭代物件,都可以直接使用這兩個運算子完成查詢。
對於資料驗證、搜尋、條件過濾等常見需求,成員運算子提供了簡潔且可讀性高的寫法,讓程式碼遠離繁瑣的迴圈與索引操作。掌握它們不僅能提升開發效率,也能降低錯誤率,是每位 Python 開發者的必備功夫。
核心概念
1. 基本語法
element in container # 若 element 出現在 container 中,回傳 True
element not in container # 若 element 不在 container 中,回傳 True
container可以是 字串、列表、元組、集合、字典的鍵,甚至是自訂的可迭代類別。- 運算子本身會在背後呼叫容器的
__contains__方法,若未實作則會退回逐項比對(O(n))。
2. 應用於不同資料型別
| 容器類型 | 範例 | 說明 |
|---|---|---|
| 字串 | "a" in "apple" |
判斷子字串是否出現在另一字串中 |
| 列表 | 3 in [1, 2, 3, 4] |
檢查元素是否為列表成員 |
| 元組 | ('x',) in (('a',), ('x',)) |
同列表,只是不可變 |
| 集合 | 5 in {1, 2, 3, 4, 5} |
集合的查找時間為 O(1) |
| 字典 | 'key' in {'key': 1, 'val': 2} |
預設檢查 鍵 是否存在 |
3. 成員運算子與迭代的關係
當容器沒有實作 __contains__ 時,Python 會將 in 轉換為以下等價的迭代方式:
any(element == item for item in container)
這表示即使是自訂類別,只要實作 __iter__,仍可使用 in 進行成員檢查,只是效能會退化為線性搜尋。
程式碼範例
以下範例展示 in / not in 在實務開發中的常見用法,並附上說明註解。
範例 1:字串搜尋與大小寫處理
# 判斷使用者輸入的關鍵字是否在說明文字中
description = "Python 提供了強大的文字處理功能。"
keyword = "文字"
# 直接使用 in,大小寫敏感
if keyword in description:
print("找到關鍵字!")
else:
print("關鍵字不在說明內。")
# 若要忽略大小寫,可先統一轉為小寫
if keyword.lower() in description.lower():
print("(忽略大小寫)找到關鍵字!")
重點:字串的
in搜尋是 O(m·n)(m 為關鍵字長度,n 為母字串長度),但在日常使用上已足夠快速。
範例 2:列表過濾與條件判斷
# 假設有一組使用者 ID,想檢查是否已在黑名單中
blacklist = [1001, 2034, 3056, 4099]
def is_allowed(user_id):
# 使用 not in 判斷是否不在黑名單
return user_id not in blacklist
# 測試
for uid in [1001, 2000, 3056, 8000]:
print(f"User {uid} -> {'允許' if is_allowed(uid) else '拒絕'}")
技巧:如果黑名單資料量龐大,改用 集合 (
set) 可將查找時間從 O(n) 降至 O(1)。
範例 3:集合的快速成員測試
# 建立一個包含所有有效指令的集合
VALID_COMMANDS = {"start", "stop", "pause", "resume"}
def execute(command):
if command in VALID_COMMANDS:
print(f"執行指令: {command}")
else:
print(f"未知指令: {command}")
# 測試
execute("start")
execute("reset")
說明:集合的
in檢查背後使用雜湊表,效能極佳,適合大量字串或數值的快速驗證。
範例 4:字典鍵檢查與預設值
# 使用者設定檔
profile = {
"name": "Alice",
"email": "alice@example.com",
# "age" 欄位可能缺失
}
# 檢查鍵是否存在,若不存在給予預設值
age = profile["age"] if "age" in profile else 18
print(f"使用者年齡: {age}")
# 更 Pythonic 的寫法:dict.get()
age = profile.get("age", 18)
print(f"使用者年齡(get): {age}")
提示:對字典使用
in時,只會檢查 鍵,不會檢查值。
範例 5:自訂可迭代類別的 in 支援
class MyRange:
"""模擬內建 range,但只支援正向迭代"""
def __init__(self, start, stop):
self.start = start
self.stop = stop
def __iter__(self):
current = self.start
while current < self.stop:
yield current
current += 1
# 使用 in 進行成員測試
r = MyRange(1, 5) # 產生 1,2,3,4
print(3 in r) # True
print(5 in r) # False
說明:即使未實作
__contains__,只要提供__iter__,in仍可正常工作,只是效能會是線性搜尋。
常見陷阱與最佳實踐
| 陷阱 | 可能的錯誤 | 建議的做法 |
|---|---|---|
| 大小寫不一致 | "A" in "abc" 永遠回傳 False |
先統一 lower() 或 upper(),或使用正規表達式 |
| 把字典值當鍵檢查 | value in my_dict 只會比對鍵 |
若要檢查值,使用 value in my_dict.values() |
| 在大資料集合使用列表 | x in large_list 會變成 O(n) |
改用 set 或 dict 以獲得 O(1) 查找 |
忘記 not in 的優先度 |
if not a in b: 被解讀為 if (not a) in b: |
使用括號 if a not in b:,或直接寫 if not (a in b): |
對非可迭代物件使用 in |
3 in 5 會拋出 TypeError |
確認左側是可迭代容器,或使用 hasattr(obj, '__contains__') 事先檢查 |
最佳實踐:
優先選擇集合 (
set) 進行「是否存在」的判斷,特別是資料量大且查詢頻繁的情況。使用
dict.get()取代key in dict+ 索引的寫法,讓程式更簡潔且避免KeyError。保持語意一致:
in用於「存在」,not in用於「不存在」,盡量避免在同一行混用not與in造成閱讀困難。善用型別註記(Python 3.9+)提升 IDE 靜態檢查,減少誤用:
def has_permission(user_id: int, allowed: set[int]) -> bool: return user_id in allowed
實際應用場景
API 請求參數驗證
在處理 RESTful API 時,常會檢查傳入的action是否屬於允許的指令集合:ALLOWED_ACTIONS = {"create", "update", "delete"} if request.json.get("action") not in ALLOWED_ACTIONS: raise ValueError("不支援的操作")資料清理與去重
讀取 CSV 時,可利用set追蹤已出現的 ID,快速過濾重複資料:seen_ids = set() for row in csv_reader: if row["id"] in seen_ids: continue # 重複,直接跳過 seen_ids.add(row["id"]) process(row)使用者權限檢查
角色權限往往以列表或集合方式儲存,判斷使用者是否具備某個功能:user_roles = {"admin", "editor"} if "admin" in user_roles: show_admin_panel()文字過濾
建立敏感字詞集合,檢查訊息內容是否包含禁用詞彙:BLOCKED_WORDS = {"spam", "廣告", "詐騙"} def is_clean(message: str) -> bool: return not any(word in message for word in BLOCKED_WORDS)
總結
in/not in是 Python 中檢查「元素是否屬於」容器的核心運算子,支援多種可迭代型別。- 透過 集合 (
set) 可將查詢時間從線性 (O(n)) 降至常數 (O(1));字典則僅檢查鍵。 - 常見的陷阱包括大小寫不一致、誤用字典值、在大列表上使用線性搜尋等,遵循最佳實踐能顯著提升效能與可讀性。
- 成員運算子在 API 驗證、權限管理、資料清理、文字過濾 等實務情境中扮演關鍵角色,是每位 Python 開發者不可或缺的工具。
掌握 in 與 not in,不僅能讓程式碼更簡潔,同時也能在大量資料處理時保持高效、可靠。祝你在 Python 的旅程中,玩得開心、寫得順手!