耐人尋味的 for...else...語句
老婆給當程序員的老公打電話:“下班順路買一斤包子帶回來,如果看到賣西瓜的,買一個。”當晚,程序員老公手捧一個包子進了家門……老婆怒道:“你怎么就買了一個包子?”老公答曰:“因為看到了賣西瓜的。”
程序員買西瓜的笑話可能大部分讀者都知道,今天寫的這篇文章和這個笑話有一定的關系。
任何編程語言都提供了 if…else… 語句,表示如果(if)滿足條件就做某件事,否則(else)就做另外一件事:
- if a==b:
- print("true")
- else:
- print("false")
然而,在 Python 中 else 不僅可以和 if 搭配使用,另一種特有的句法是 for…else …,它還可以和 while、try…except 組合使用,例如:
- for i in range(3):
- print(i)
- else:
- print("end")
- >>>
- 0
- 1
- 2
- end
但是,你會發現 for…else… 與 if…else… 表現得不一樣,按照以往經驗來說,執行了 for 語句塊的代碼就不執行 else 里面了,反之亦然。
然而,我們看到的卻恰恰相反,for 循環結束之后接著又執行了 else 語句塊,這就有點意思了,if … else … 翻譯成大白話就是 如果…否則…,而 for…else… 翻譯成白話成了 直到… 然后 …,為什么不把它寫成 for…then… 的句式呢?
for 循環遍歷空列表也會執行 else 語句塊,因為它是正常退出 for 循環的一種特例情況。
- for i in []:
- print(i)
- else:
- print("end")
- >>>
- 0
繼續探索,我們用 break 提前終止 for 循環
- for i in range(3):
- print(i)
- if i % 2 == 0:
- break
- else:
- print("end")
- >>>
- 0
循環遇到 break 退出后,整個語句就結束,else 語句塊也不執行了。
綜上,我們可以得出這樣一個結論,只有當循環里沒有遇到 break 時,else 塊才會執行。
Python 之父為什么要搞出這樣的一種語法糖出來呢?這是我們常人沒法理解的。不過「python之禪」告訴了我們答案: “Although that way may not be obvious at first unless you’re Dutch.”。
帶著這個問題,我也在 StackOver Flow 找了一下答案,在平時的開發中真的很少有 for…else… 的應用場景,不過,像下面這種場景用 for else 還真是一種 pythonic 的用法。
當你用 for 循環迭代查找列表的中的某個元素時,如果找到了就提前退出,如果迭代完了還沒找到需要以另外一種形式通知調用者時,用 for else 無疑是***的選擇。
- for i in mylist:
- if i == target:
- break
- process(i)
- else:
- raise ValueError("List argument missing terminal flag.")
如果不用 for…else… , 那么還需要專門建立一個臨時標記變量來標記是否已經找到了
- found = False
- for i in mylist:
- if i == target:
- found = True
- break
- process(i)
- if not found:
- raise ValueError("List argument missing terminal flag.")
當你想在房間里找某樣東西時,只要在任意位置找到了,就停止繼續搜查工作。但如果把整個房間都翻遍了,還沒找到我們想要的東西,需要告訴人家說:這兒沒有你要找的東西。遇到這樣的情況用 for … else ,除此之外,***不要用它。
【本文是51CTO專欄作者“劉志軍”的原創文章,作者微信公眾號:Python之禪(VTtalk)】