# Python 推導式(Comprehension)與賦值表達式(Walrus Operator)
如有錯誤歡迎糾正,小弟正在學習中
## 推導式(comprehension)
推導式是python獨有的方法可簡潔for loop,來看例子
正常情況for loop
### list
``` python
greater_than_five = []
numbers = [1,2,3,4,5,6,7,8,9,10]
for number in numbers:
if number > 5:
greater_than_five.append(number)
```
從程式中簡單的邏輯卻不太簡潔,我們可以漂亮的改用推導式
``` python
numbers = [1,2,3,4,5,6,7,8,9,10]
greater_than_five= [n for n in numbers
if n > 5]
print(greater_than_five)
>> [6, 7, 8, 9, 10]
```
推導式好處在將結果賦予變數,不須再for loop外層宣告變數,也不用再用append加入list,簡潔了程式
### dict
``` python
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
greater_than_five = {f'loop_{n}': n for n in numbers
if n > 5}
print(greater_than_five)
>>> {'loop_6': 6, 'loop_7': 7, 'loop_8': 8, 'loop_9': 9, 'loop_10': 10}
```
### 推導式須注意部分
推導式好用,但有些部分要注意
- 過多迴圈避免使用推導式
如果for loop超過三個就不建議用推導式了,在閱讀上與維護會相當複雜
三個for loop用推導式簡直是地獄
``` python
# 使用推導式生成三維列表,並在每層嵌套中進行條件判斷
result = [[[i * j * k for k in range(3) if i * j * k % 2 == 0] for j in range(3)] for i in range(3)]
```
正常foor loop,較上面例子看起來好閱讀一些
```
result = []
for i in range(3):
layer = []
for j in range(3):
row = []
for k in range(3):
value = i * j * k
if value % 2 == 0:
row.append(value)
layer.append(row)
result.append(layer)
```
> 💡補充知識:在程式上請避免縮排大於三,迴圈也盡量避免超過二,避免增加閱讀上困難,如果沒辦法避免也請用[Early Retrurn Pattern](https://hackmd.io/4H1J5p1GTOG3f90I7u-bRA)來處理
- 需評估判斷式是否會增新增條件
當判斷條件為複數或以上就不推薦使用推導式,尤其是預期未來業務邏輯會增加,那就會讓閱讀上與程式更加混亂與擴充邏輯
判斷式條件複數時難以閱讀,個人習慣是條件大於三不使用推導式,如果複數條件會看條件複雜性來決定是否使用推導式,以下例子對於python不熟悉的人閱讀會比較吃力,但以條件來看複雜性不算高還能接受
``` python
data = [
{"name": "Alice", "age": 25, "city": "New York"},
{"name": "Bob", "age": 30, "city": "San Francisco"},
{"name": "Charlie", "age": 35, "city": "New York"},
{"name": "David", "age": 40, "city": "Chicago"}
]
# 複雜條件:選擇年齡大於 30 且城市為 New York 的人
filtered = [person for person in data if person["age"] > 30 and person["city"] == "New York"]
```
相較於單純用for loop還比較清晰
``` python
filtered = []
for person in data:
if person["age"] > 30 and person["city"] == "New York":
filtered.append(person)
```
- 縮排需要注意,避免增加閱讀上的困難
例子:篩選合格的學生成績
假設我們有一組學生的考試成績數據,想要篩選出所有及格的成績(及格分數為 60 分及以上),並將這些成績加入到一個新列表中。
沒推導式的寫法
``` python
grades = [
[55, 78, 62],
[88, 45, 91],
[76, 58, 73]
]
passing_grades = []
for student_grades in grades: # 外層迴圈,遍歷每一個學生的成績
for grade in student_grades: # 內層迴圈,遍歷該學生的每一科成績
if grade >= 60: # 如果成績及格
passing_grades.append(grade) # 將及格成績加入新列表
```
改成推導式但沒排版,非常難以閱讀
``` python
grades = [
[55, 78, 62],
[88, 45, 91],
[76, 58, 73]
]
passing_grades = [grade for student_grades in grades for grade in student_grades if grade >= 60]
print(passing_grades)
```
來看有排版部分,這是我個人習慣,一行只會有一個for loop或if,與上面對比清晰好閱讀
``` python
grades = [
[55, 78, 62],
[88, 45, 91],
[76, 58, 73]
]
passing_grades = [grade for student_grades in grades
for grade in student_grades
if grade >= 60]
print(passing_grades)
```
> 💡迴圈寫法是由上而下,而推導式相反會由下至上,這邊可能要注意
### 推導式好處
- 程式簡潔
- 需要for loop量大的數據會增加效能,因為for loop call append()降低效能
## 賦值表達式
Python 3.8 才有的功能,也被稱為海象運算符(Walrus Operator),符號為 :=(很像象牙)。它允許在表達式中進行賦值操作,來實際舉例會比較易懂
例子: 需要檢查使用者輸入的長度是否大於5,如果小於5也請告知使用者長度還少多少
沒有用賦值表達是寫法
``` python
user_input = input("Enter a string: ")
length = len(user_input)
if length > 5:
print(f"Input is long enough: {user_input}")
else:
print(f"Input is too short. Needs {5 - length} more characters.")
```
使用賦值表達式可以省去變數宣告,也簡潔了程式碼
``` python
if (user_input := input("Enter a string: ")):
length = len(user_input)
if length > 5:
print(f"Input is long enough: {user_input}")
else:
print(f"Input is too short. Needs {5 - length} more characters.")
```
### 賦值表達式需注意部分
- 當要使用賦值表達式在運算結果需要特別注意()範圍,否則結果會不如你預期
例子1: 需要儲存5-3結果大於1然後再乘10
括號範圍: (result:=(5-3))
``` python
if (result:=(5-3)) >1:
cal = result*10
print(cal)
>>> 20
```
例子2:將外圍括號去除,會發現結果不如預期,
括號範圍:無
結果: result 值會被賦值為 (5-3) > 1 result 會是 bool True,True*10 結果就會是10
``` python
if result:=(5-3) >1:
cal = result*10
print(cal)
>>> 10
```
例子3
括號範圍:(result:=(5-3) >1)
結果: 括號條件result也是bool True,所以最終 cal為 10
``` python
if (result:=(5-3) >1):
cal = result*10
print(cal)
```
### 賦值表達式優缺點
優點
- 簡潔省去多餘的變數宣告
缺點
- 不熟悉python 3.8的開發者可能看不懂
[1] stackoverflow - Why is a list comprehension so much faster than appending to a list?https://stackoverflow.com/questions/30245397/why-is-a-list-comprehension-so-much-faster-than-appending-to-a-list