Python 進階技巧:海象運算子 (Walrus Operator) 實戰教學 [w_clean for w in words if (w_clean:=w.lower().strip()) and w_clean not in  STOPWORDS]

加入好友
加入社群
Python 進階技巧:海象運算子 (Walrus Operator) 實戰教學 [w_clean for w in words if (w_clean:=w.lower().strip()) and w_clean not in  STOPWORDS] - 儲蓄保險王

在 Python 3.8 引入的「海象運算子 (`:=`)」是一個強大的工具,
能在表達式內部進行變數賦值。
這一特性在資料清洗(Etl)場景中特別有用,
能讓程式碼同時保持「高效」與「簡潔」。

## 1. 實戰場景:髒亂的資料

假設我們有一組從文檔中切分出來的原始字串 `words`,其中包含:

1.  **NIC, MAC, TEST**: 有效的關鍵字,但大小寫不統一。

2.  **”   “**: 無意義的空白字串(切割雜訊)。

3.  **replaced**: 停用詞(Stopwords),這是檔名中多餘的後綴,我們不想檢索它。

# 模擬輸入資料
words = ["NIC", "MAC       ", "   ", "TEST", "Replaced"]

# 設定停用詞
STOPWORDS = {"replaced"}

我們的目標是:

1.  **正規化 (Normalize)**: 去除前後空白 `strip()` 並轉小寫 `lower()`。

2.  **過濾空值**: 處理後如果是空字串(如原本是 `”   “`),要丟掉。

3.  **過濾雜訊**: 處理後如果是停用詞(如 `replaced`),要丟掉。

## 2. 寫法大對決

### ❌ 寫法 A:效率低落的列表推導式

如果不使用海象運算子,為了在 `if` 中判斷處理後的結果,我們被迫**重複計算**

tokens = [
    w.strip().lower() for w in words  # <--- 第三次計算 (為了存入列表)
    if w.strip().lower()              # <--- 第一次計算 (判斷是否為空)
    and w.strip().lower() not in STOPWORDS # <--- 第二次計算 (判斷是否為停用詞)
]
# 缺點對每個字串重複執行了 2~3  strip().lower(),浪費 CPU 資源
Python 進階技巧:海象運算子 (Walrus Operator) 實戰教學 [w_clean for w in words if (w_clean:=w.lower().strip()) and w_clean not in  STOPWORDS] - 儲蓄保險王

### ❌ 寫法 B:傳統迴圈 (For Loop)

這是最穩健的寫法,但略顯冗長。

tokens = []
for w in words:
    w_clean = w.strip().lower()       # 僅計算一次
    if w_clean and w_clean not in STOPWORDS:
        tokens.append(w_clean)
# 缺點佔用了 5 行代碼不夠 Pythonic
Python 進階技巧:海象運算子 (Walrus Operator) 實戰教學 [w_clean for w in words if (w_clean:=w.lower().strip()) and w_clean not in  STOPWORDS] - 儲蓄保險王

### ✅ 寫法 C:海象運算子 (`:=`)

結合了上述兩者的優點:**只算一次,且只寫一行**

tokens = [
    w_clean for w in words 
    if (w_clean := w.strip().lower()) and w_clean not in STOPWORDS
]
Python 進階技巧:海象運算子 (Walrus Operator) 實戰教學 [w_clean for w in words if (w_clean:=w.lower().strip()) and w_clean not in  STOPWORDS] - 儲蓄保險王

### ⚠️ 常見陷阱:為什麼一定要加括號?

很多新手會不小心寫成這樣,導致 `SyntaxError`:

# ❌ 錯誤寫法 (缺少括號)
[w_clean for w in words if w_clean := w.strip().lower() and w_clean not in STOPWORDS]
# Error: SyntaxError: invalid syntax

**原因解密**

這是因為 **優先級 (Precedence)** 的問題。在 Python 中,`:=` 的優先權非常低(比 `and` 還低)。

如果不加括號,Python 會試圖這樣解讀:

`if w_clean := (w.strip().lower() and w_clean not in STOPWORDS)`

這會導致災難:

1.  Python 試圖先算 `and` 後面的東西。

2.  但此時 `w_clean` 根本還沒被賦值(變數不存在)。

3.  賦值的對象變成了 `and` 運算的布林結果,而非字串本身。

**✅ 正確解法**

務必使用 **小括號 `()`** 把賦值動作包起來,明確告訴 Python:「先把這個賦值算完,拿到字串,再來跟後面做 `and`」。

## 3.  NIC MAC TEST 逐步拆解

讓我們看看寫法 C 在執行時發生了什麼:

#### 🟢 第 1 回合:處理 `”NIC”`

1.  **運算與賦值**: `(w_clean := “NIC”.strip().lower())`

    *   計算結果為 `”nic”`。

    *   變數 `w_clean` 被賦值為 `”nic”`。

2.  **邏輯判斷 1**: `”nic”` 是非空字串 (Truthy),條件成立。➡ **繼續**

3.  **邏輯判斷 2**: `w_clean (“nic”) not in STOPWORDS`?

    *   `”nic”` **不在** `STOPWORDS` (`{“replaced”}`) 中。➡ **成立** (True)

4.  **結果**: 將 `w_clean` (`”nic”`) 加入列表。

#### 🟢 第 2 回合:處理 `”MAC       “` (帶有空白)

1.  **運算與賦值**: `(w_clean := “MAC       “.strip().lower())`

    *   計算結果為 `”mac”` (空白被 strip 掉了)。

    *   變數 `w_clean` 被賦值為 `”mac”`。

2.  **邏輯判斷 1**: `”mac”` (Truthy)。➡ **繼續**

3.  **邏輯判斷 2**: `”mac”` 不是停用詞。➡ **成立** (True)

4.  **結果**: 將 `w_clean` (`”mac”`) 加入列表。

#### 🔴 第 3 回合:處理 `”   “` (純空白)

1.  **運算與賦值**: `(w_clean := ”   “.strip().lower())`

    *   計算結果為 `””` (空字串)。

    *   變數 `w_clean` 被賦值為 `””`。

2.  **邏輯判斷 1**: `””` 是空字串 (Falsy),條件**不成立**

3.  **短路 (Short-circuit)**: 因為 `and` 前面已經是 False,程式**直接跳過**後面的 STOPWORDS 檢查。

4.  **結果**: 丟棄。

#### 🟢 第 4 回合:處理 `”TEST”` (保留字)

1.  **運算與賦值**: `(w_clean := “TEST”.strip().lower())`

    *   計算結果為 `”test”`。

    *   變數 `w_clean` 被賦值為 `”test”`。

2.  **邏輯判斷 1**: `”test”` (Truthy)。➡ **繼續**

3.  **邏輯判斷 2**: `w_clean (“test”) not in STOPWORDS`?

    *   `”test”` **不在** `STOPWORDS` (`{“replaced”}`) 中。➡ **成立** (True)

4.  **結果**: 將 `w_clean` (`”test”`) 加入列表。

#### 🔴 第 5 回合:處理 `”Replaced”` (停用詞)

1.  **運算與賦值**: `(w_clean := “Replaced”.strip().lower())`

    *   計算結果為 `”replaced”`。

    *   變數 `w_clean` 被賦值為 `”replaced”`。

2.  **邏輯判斷 1**: `”replaced”` (Truthy) 且非空。➡ **繼續**

3.  **邏輯判斷 2**: `w_clean (“replaced”) not in STOPWORDS`?

    *   `”replaced”` **在** `STOPWORDS` 中,所以 `not in` 為 **False**

4.  **結果**: 丟棄。

## 4. 關鍵解密:海象運算子表達式的雙重身分

為什麼 `if (w_clean := w.strip().lower())` 這行代碼可以運作?因為 `:=` 其實同時做了兩件事:

**第一件事:賦值 (Assign)**

先把 `w.strip().lower()` 的計算結果,存進變數 `w_clean` 裡。

> 此時 `w_clean` 已經有值了(可能是 `”nic”`,也可能是 `””`)。

**第二件事:回傳 (Return)**

把剛剛算出來的那個值,「交出來」給外層的 `if` 進行檢查。

所以這行代碼翻譯成人類語言就是:

**把清洗結果存給 `w_clean`,然後檢查這個 `w_clean` 是不是真的(非空)?**

*   如果結果是 `”nic”` ➔ 存入變數 ➔ `if “nic”` ➔ **真 (Truthy)** ➔ 繼續往後執行 (`and …`)。

*   如果結果是 `””` ➔ 存入變數 ➔ `if “”` ➔ **假 (Falsy)** ➔ 停下來,直接丟棄。

這種「存起來 + 馬上拿來用」的特性,就是海象運算子最大的價值。

## 5. 總結

使用 `if (w_clean := expression)` 的核心優勢在於:

1.  **效能 (Performance)**: 昂貴的字串操作(如 `strip`, `lower`, `regex`)只需執行一次。

2.  **可讀性 (Readability)**: 變數名稱 `w_clean` 直接告訴閱讀者我們在使用「清洗後的版本」進行判斷。

3.  **簡潔 (Conciseness)**: 保持了列表推導式的簡潔美感。

推薦hahow線上學習python: https://igrape.net/30afN

## 5. 同場加映:海象運算子的其他應用場景

除了在 `if` 或列表推導式中使用外,
`:=` 在 `while` 迴圈中也非常強大,
特別是用來簡化「讀取直到結束」的模式。

### 場景:讀取檔案直到空白行

**傳統寫法** (需要重複寫 `readline`):

fp = open("data.txt")
line = fp.readline()  # 先讀第一行
while line:           # 檢查是否有內容
    print(line.strip())
    line = fp.readline() # 迴圈最後再讀下一行 (重複代碼!)

**海象寫法** (優雅的一行流):

fp = open("data.txt")
while (line := fp.readline()): #讀取並且指派給 line, 同時檢查 line 是否為 truthy
    print(line.strip())

### 場景:正則表達式 (Regex) 匹配

**傳統寫法**

import re
match = re.search(r'(\d+)', 'Age: 25') # 先匹配
if match:                              # 再檢查
    print(match.group(1))

**海象寫法**

if match := re.search(r'(\d+)', 'Age: 25'): # 匹配+賦值+檢查 一次搞定
    print(match.group(1))                   # 直接使用 match 變數

## 6. 總結

使用 `if (w_clean := expression)` 的核心優勢在於:

1.  **效能 (Performance)**: 昂貴的字串操作(如 `strip`, `lower`, `regex`)只需執行一次。

2.  **可讀性 (Readability)**: 變數名稱 `w_clean` 直接告訴閱讀者我們在使用「清洗後的版本」進行判斷。

3.  **簡潔 (Conciseness)**: 保持了列表推導式的簡潔美感。

推薦hahow線上學習python: https://igrape.net/30afN

加入好友
加入社群
Python 進階技巧:海象運算子 (Walrus Operator) 實戰教學 [w_clean for w in words if (w_clean:=w.lower().strip()) and w_clean not in  STOPWORDS] - 儲蓄保險王

儲蓄保險王

儲蓄險是板主最喜愛的儲蓄工具,最喜愛的投資理財工具則是ETF,最喜愛的省錢工具則是信用卡

You may also like...

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *