基本功:條件與循環-day5

基本功:條件與循環

寫在前面

你好,我是禪墨!

哈哈哈,堅持第五天!今天的很重要!

前面幾節,一起學習了列表、元組、字典、集合和字符串等一系列 Python 的基本數據類型。但是,如何把這一個個基本的數據結構類型串接起來,組成一手漂亮的代碼呢?這就是我們今天所要討論的“條件與循環”。

我習慣把“條件與循環”,叫做編程中的基本功。爲什麼稱它爲基本功呢?因爲它控制着代碼的邏輯,可以說是程序的中樞系統。如果把寫程序比作蓋樓房,那麼條件與循環就是樓房的根基,其他所有東西都是在此基礎上構建而成。毫不誇張地說,寫一手簡潔易讀的條件與循環代碼,對提高程序整體的質量至關重要。

條件語句

首先,我們一起來看一下 Python 的條件語句,用法很簡單。比如,我想要表示 y=|x|這個函數,那麼相應的代碼便是:


# y = |x|
if x < 0:
    y = -x
else:
    y = x

和其他語言不一樣,我們不能在條件語句中加括號,寫成下面這樣的格式。

if (x < 0)

但需要注意的是,在條件語句的末尾必須加上冒號(:),這是 Python 特定的語法規範。

由於 Python 不支持 switch 語句,因此,當存在多個條件判斷時,我們需要用 else if 來實現,這在 Python 中的表達是 elif。語法如下:


if condition_1:
    statement_1
elif condition_2:
    statement_2
...
elif condition_i:
    statement_i
else:
    statement_n

不過要注意,if 語句是可以單獨使用的,但 elif、else 都必須和 if 成對使用。另外,在我們進行條件判斷時, 不少人喜歡省略判斷的條件,比如寫成下面這樣:


if s: # s is a string
    ...
if l: # l is a list
    ...
if i: # i is an int
    ...
... 

關於省略判斷條件的常見用法,我大概總結了一下:

在這裏插入圖片描述

不過,切記,在實際寫代碼時,我們鼓勵,除了 boolean 類型的數據,條件判斷最好是顯性的。比如,在判斷一個整型數是否爲 0 時,我們最好寫出判斷的條件:

if i != 0:

而不是只寫出變量名:

if i:

循環語句

講完了條件語句,我們接着來看循環語句。所謂循環,顧名思義,本質上就是遍歷集合中的元素。和其他語言一樣,Python 中的循環一般通過 for 循環和 while 循環實現。

比如,我們有一個列表,需要遍歷列表中的所有元素並打印輸出,代碼如下:


l = [1, 2, 3, 4]
for item in l:
    print(item)
1
2
3
4

你看,是不是很簡單呢?

其實,Python 中的數據結構只要是可迭代的(iterable),比如列表、集合等等,那麼都可以通過下面這種方式遍歷:

for item in :

這裏需要單獨強調一下字典。字典本身只有鍵是可迭代的,如果我們要遍歷它的值或者是鍵值對,就需要通過其內置的函數 values() 或者 items() 實現。其中,values() 返回字典的值的集合,items() 返回鍵值對的集合。


d = {'name': 'jason', 'dob': '2000-01-01', 'gender': 'male'}
for k in d: # 遍歷字典的鍵
    print(k)
name
dob
gender

for v in d.values(): # 遍歷字典的值
    print(v)
jason
2000-01-01
male    

for k, v in d.items(): # 遍歷字典的鍵值對
    print('key: {}, value: {}'.format(k, v))
key: name, value: jason
key: dob, value: 2000-01-01
key: gender, value: male 

看到這裏你也許會問,有沒有辦法通過集合中的索引來遍歷元素呢?當然可以,其實這種情況在實際工作中還是很常見的,甚至很多時候,我們還得根據索引來做一些條件判斷。

我們通常通過 range() 這個函數,拿到索引,再去遍歷訪問集合中的元素。比如下面的代碼,遍歷一個列表中的元素,當索引小於 5 時,打印輸出:


l = [1, 2, 3, 4, 5, 6, 7]
for index in range(0, len(l)):
    if index < 5:
        print(l[index])        
        
1
2
3
4
5

當我們同時需要索引和元素時,還有一種更簡潔的方式,那就是通過 Python 內置的函數 enumerate()。用它來遍歷集合,不僅返回每個元素,並且還返回其對應的索引,這樣一來,上面的例子就可以寫成:


l = [1, 2, 3, 4, 5, 6, 7]
for index, item in enumerate(l):
    if index < 5:
        print(item)  
              
1
2
3
4
5

在循環語句中,我們還常常搭配 continue 和 break 一起使用。所謂 continue,就是讓程序跳過當前這層循環,繼續執行下面的循環;而 break 則是指完全跳出所在的整個循環體。在循環中適當加入 continue 和 break,往往能使程序更加簡潔、易讀。

比如,給定兩個字典,分別是產品名稱到價格的映射,和產品名稱到顏色列表的映射。我們要找出價格小於 1000,並且顏色不是紅色的所有產品名稱和顏色的組合。如果不用 continue,代碼應該是下面這樣的:


# name_price: 產品名稱(str)到價格(int)的映射字典
# name_color: 產品名字(str)到顏色(list of str)的映射字典
for name, price in name_price.items():
    if price < 1000:
        if name in name_color:
            for color in name_color[name]:
                if color != 'red':
                    print('name: {}, color: {}'.format(name, color))
        else:
            print('name: {}, color: {}'.format(name, 'None'))

而加入 continue 後,代碼顯然清晰了很多:


# name_price: 產品名稱(str)到價格(int)的映射字典
# name_color: 產品名字(str)到顏色(list of str)的映射字典
for name, price in name_price.items():
    if price >= 1000:
        continue
    if name not in name_color:
        print('name: {}, color: {}'.format(name, 'None'))
        continue
    for color in name_color[name]:
        if color == 'red':
            continue
        print('name: {}, color: {}'.format(name, color))

我們可以看到,按照第一個版本的寫法,從開始一直到打印輸出符合條件的產品名稱和顏色,共有 5 層 for 或者 if 的嵌套;但第二個版本加入了 continue 後,只有 3 層嵌套。

顯然,如果代碼中出現嵌套裏還有嵌套的情況,代碼便會變得非常冗餘、難讀,也不利於後續的調試、修改。因此,我們要儘量避免這種多層嵌套的情況。

前面講了 for 循環,對於 while 循環,原理也是一樣的。它表示當 condition 滿足時,一直重複循環內部的操作,直到 condition 不再滿足,就跳出循環體。

while condition:

很多時候,for 循環和 while 循環可以互相轉換,比如要遍歷一個列表,我們用 while 循環同樣可以完成:


l = [1, 2, 3, 4]
index = 0
while index < len(l):
    print(l[index])
    index += 1

那麼,兩者的使用場景又有什麼區別呢?

通常來說,如果你只是遍歷一個已知的集合,找出滿足條件的元素,並進行相應的操作,那麼使用 for 循環更加簡潔。但如果你需要在滿足某個條件前,不停地重複某些操作,並且沒有特定的集合需要去遍歷,那麼一般則會使用 while 循環。

比如,某個交互式問答系統,用戶輸入文字,系統會根據內容做出相應的回答。爲了實現這個功能,我們一般會使用 while 循環,大致代碼如下:


while True:
    try:
        text = input('Please enter your questions, enter "q" to exit')
        if text == 'q':
            print('Exit system')
            break
        ...
        ...
        print(response)
    except as err:
        print('Encountered error: {}'.format(err))
        break 

同時需要注意的是,for 循環和 while 循環的效率問題。比如下面的 while 循環:


i = 0
while i < 1000000:
    i += 1

和等價的 for 循環:


for i in range(0, 1000000):
    pass

究竟哪個效率高呢?

要知道,range() 函數是直接由 C 語言寫的,調用它速度非常快。而 while 循環中的“i += 1”這個操作,得通過 Python 的解釋器間接調用底層的 C 語言;並且這個簡單的操作,又涉及到了對象的創建和刪除(因爲 i 是整型,是 immutable,i += 1 相當於 i = new int(i + 1))。所以,顯然,for 循環的效率更勝一籌。

條件與循環往復

前面兩部分講了條件與循環的一些基本操作,接下來,我們重點來看它們的進階操作,讓程序變得更簡潔高效。

在閱讀代碼的時候,你應該常常會發現,有很多將條件與循環並做一行的操作,例如:

expression1 if condition else expression2 for item in iterable

將這個表達式分解開來,其實就等同於下面這樣的嵌套結構:


for item in iterable:
    if condition:
        expression1
    else:
        expression2

而如果沒有 else 語句,則需要寫成:

expression for item in iterable if condition

舉個例子,比如我們要繪製 y = 2*|x| + 5 的函數圖像,給定集合 x 的數據點,需要計算出 y 的數據集合,那麼只用一行代碼,就可以很輕鬆地解決問題了:


y = [value * 2 + 5 if value > 0 else -value * 2 + 5 for value in x]

再比如我們在處理文件中的字符串時,常常遇到的一個場景:將文件中逐行讀取的一個完整語句,按逗號分割單詞,去掉首位的空字符,並過濾掉長度小於等於 5 的單詞,最後返回由單詞組成的列表。這同樣可以簡潔地表達成一行:


text = ' Today,  is, Sunday'
text_list = [s.strip() for s in text.split(',') if len(s.strip()) > 5]
print(text_list)
['Today', 'Sunday']

當然,這樣的複用並不僅僅侷限於一個循環。比如,給定兩個列表 x、y,要求返回 x、y 中所有元素對組成的元組,相等情況除外。那麼,你也可以很容易表示出來:

[(xx, yy) for xx in x for yy in y if xx != yy]

這樣的寫法就等價於:


l = []
for xx in x:
    for yy in y:
        if xx != yy:
            l.append((xx, yy))

熟練之後,你會發現這種寫法非常方便。當然,如果遇到邏輯很複雜的複用,你可能會覺得寫成一行難以理解、容易出錯。那種情況下,用正常的形式表達,也不失爲一種好的規範和選擇。

寫在後面

有元素對組成的元組,相等情況除外。那麼,你也可以很容易表示出來:

[(xx, yy) for xx in x for yy in y if xx != yy]

這樣的寫法就等價於:


l = []
for xx in x:
    for yy in y:
        if xx != yy:
            l.append((xx, yy))

熟練之後,你會發現這種寫法非常方便。當然,如果遇到邏輯很複雜的複用,你可能會覺得寫成一行難以理解、容易出錯。那種情況下,用正常的形式表達,也不失爲一種好的規範和選擇。

寫在後面

今天的分享就到這裏了!
點個贊再走吧,有什麼建議歡迎給我留言!
知乎:禪墨雲
CSDN:禪墨雲
微信公衆:興趣路人甲

這裏是引用

個人博客:禪墨雲
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章