本文 AI 產出,尚未審核

Python 課程 – 運算子(Operators)

主題:成員運算子(in / not in


簡介

在 Python 中,成員運算子innot 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) 改用 setdict 以獲得 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__') 事先檢查

最佳實踐

  1. 優先選擇集合 (set) 進行「是否存在」的判斷,特別是資料量大且查詢頻繁的情況。

  2. 使用 dict.get() 取代 key in dict + 索引的寫法,讓程式更簡潔且避免 KeyError

  3. 保持語意一致in 用於「存在」,not in 用於「不存在」,盡量避免在同一行混用 notin 造成閱讀困難。

  4. 善用型別註記(Python 3.9+)提升 IDE 靜態檢查,減少誤用:

    def has_permission(user_id: int, allowed: set[int]) -> bool:
        return user_id in allowed
    

實際應用場景

  1. API 請求參數驗證
    在處理 RESTful API 時,常會檢查傳入的 action 是否屬於允許的指令集合:

    ALLOWED_ACTIONS = {"create", "update", "delete"}
    if request.json.get("action") not in ALLOWED_ACTIONS:
        raise ValueError("不支援的操作")
    
  2. 資料清理與去重
    讀取 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)
    
  3. 使用者權限檢查
    角色權限往往以列表或集合方式儲存,判斷使用者是否具備某個功能:

    user_roles = {"admin", "editor"}
    if "admin" in user_roles:
        show_admin_panel()
    
  4. 文字過濾
    建立敏感字詞集合,檢查訊息內容是否包含禁用詞彙:

    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 開發者不可或缺的工具。

掌握 innot in,不僅能讓程式碼更簡潔,同時也能在大量資料處理時保持高效、可靠。祝你在 Python 的旅程中,玩得開心、寫得順手!