語句(4)

2018-02-24 15:48 更新

for循環(huán)在python中應用廣泛,所以,要用更多的篇幅來介紹。

并行迭代

關于迭代,在《列表(2)》中曾經提到過“可迭代的(iterable)”這個詞,并給予了適當解釋,這里再次提到“迭代”,說明它在python中占有重要的位置。

迭代,在python中表現就是用for循環(huán),從序列對象中獲得一定數量的元素。

在前面一節(jié)中,用for循環(huán)來獲得列表、字符串、元組,乃至于字典的鍵值對,都是迭代。

現實中迭代不都是那么簡單的,比如這個問題:

問題:有兩個列表,分別是:a = [1,2,3,4,5], b = [9,8,7,6,5],要計算這兩個列表中對應元素的和。

解析:

太簡單了,一看就知道結果了。

很好,這是你的方法,如果是computer姑娘來做,應該怎么做呢?

觀察發(fā)現兩個列表的長度一樣,都是5。那么對應元素求和,就是相同的索引值對應的元素求和,即a[i]+b[i],(i=0,1,2,3,4),這樣一個一個地就把相應元素和求出來了。當然,要用for來做這個事情了。

>>> a = [1,2,3,4,5]
>>> b = [9,8,7,6,5]
>>> c = []
>>> for i in range(len(a)):
...     c.append(a[i]+b[i])
... 
>>> c
[10, 10, 10, 10, 10]

看來for的表現還不錯。不過,這種方法雖然解決問題了,但python總不會局限于一個解決之道。于是又有一個內建函數zip(),可以讓同樣的問題有不一樣的解決途徑。

zip是什么東西?在交互模式下用help(zip),得到官方文檔是:

zip(...) zip(seq1 [, seq2 [...]]) -> [(seq1[0], seq2[0] ...), (...)]

Return a list of tuples, where each tuple contains the i-th element from each of the argument sequences. The returned list is truncated in length to the length of the shortest argument sequence.

seq1, seq2分別代表了序列類型的數據。通過實驗來理解上面的文檔:

>>> a = "qiwsir"
>>> b = "github"
>>> zip(a,b)
[('q', 'g'), ('i', 'i'), ('w', 't'), ('s', 'h'), ('i', 'u'), ('r', 'b')]

如果序列長度不同,那么就以"the length of the shortest argument sequence"為準。

>>> c = [1,2,3]
>>> d = [9,8,7,6]
>>> zip(c,d)
[(1, 9), (2, 8), (3, 7)]

>>> m = {"name","lang"}  
>>> n = {"qiwsir","python"}
>>> zip(m,n)
[('lang', 'python'), ('name', 'qiwsir')]

m,n是字典嗎?當然不是。下面的才是字典呢。

>>> s = {"name":"qiwsir"}
>>> t = {"lang":"python"}
>>> zip(s,t)
[('name', 'lang')]

zip是一個內置函數,它的參數必須是某種序列數據類型,如果是字典,那么鍵視為序列。然后將序列對應的元素依次組成元組,做為一個list的元素。

下面是比較特殊的情況,參數是一個序列數據的時候,生成的結果樣子:

>>> a  
'qiwsir'
>>> c  
[1, 2, 3]
>>> zip(c)
[(1,), (2,), (3,)]
>>> zip(a)
[('q',), ('i',), ('w',), ('s',), ('i',), ('r',)]

很好的zip()!那么就用它來解決前面那個兩個列表中值對應相加吧。

>>> d = []
>>> for x,y in zip(a,b):
...     d.append(x+y)
... 
>>> d
[10, 10, 10, 10, 10]

多么優(yōu)雅的解決!

比較這個問題的兩種解法,似乎第一種解法適用面較窄,比如,如果已知給定的兩個列表長度不同,第一種解法就出問題了。而第二種解法還可以繼續(xù)適用。的確如此,不過,第一種解法也不是不能修訂的。

>>> a = [1,2,3,4,5]
>>> b = ["python","www.itdiffer.com","qiwsir"]

如果已知是這樣兩個列表,要講對應的元素“加起來”。

>>> length = len(a) if len(a)<len(b) else len(b)
>>> length
3

首先用這種方法獲得兩個列表中最短的那個列表的長度。看那句三元操作,這是非常pythonic的寫法啦。寫出這句,就可以冒充高手了。哈哈。

>>> for i in range(length):
...     c.append(str(a[i]) + ":" + b[i])
... 
>>> c
['1:python', '2:www.itdiffer.com', '3:qiwsir']

我還是用第一個思路做的,經過這么修正一下,也還能用。要注意一個細節(jié),在“加”的時候,不能直接用a[i],因為它引用的對象是一個int類型,不能跟后面的str類型相加,必須轉化一下。

當然,zip()也是能解決這個問題的。

>>> d = []
>>> for x,y in zip(a,b):
...     d.append(x + y)
... 
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
TypeError: unsupported operand type(s) for +: 'int' and 'str'

報錯!看錯誤信息,我剛剛提醒的那個問題就冒出來了。所以,應該這么做:

>>> for x,y in zip(a,b):
...     d.append(str(x) + ":" + y)
... 
>>> d
['1:python', '2:www.itdiffer.com', '3:qiwsir']

這才得到了正確結果。

切記:computer是一個姑娘,她非常秀氣,需要敲代碼的小伙子們耐心地、細心地跟她相處。

以上兩種寫法那個更好呢?前者?后者?哈哈。我看差不多了。

>>> result
[(2, 11), (4, 13), (6, 15), (8, 17)]
>>> zip(*result)
[(2, 4, 6, 8), (11, 13, 15, 17)]

zip()還能這么干,是不是有點意思?

下面延伸一個問題:

問題:有一個dictionary,myinfor = {"name":"qiwsir","site":"qiwsir.github.io","lang":"python"},將這個字典變換成:infor = {"qiwsir":"name","qiwsir.github.io":"site","python":"lang"}

解析:

解法有幾個,如果用for循環(huán),可以這樣做(當然,看官如果有方法,歡迎貼出來)。

>>> infor = {}
>>> for k,v in myinfor.items():
...     infor[v]=k
... 
>>> infor
{'python': 'lang', 'qiwsir.github.io': 'site', 'qiwsir': 'name'}

下面用zip()來試試:

>>> dict(zip(myinfor.values(),myinfor.keys()))
{'python': 'lang', 'qiwsir.github.io': 'site', 'qiwsir': 'name'}

嗚呼,這是什么情況?原來這個zip()還能這樣用。是的,本質上是這么回事情。如果將上面這一行分解開來,看官就明白其中的奧妙了。

>>> myinfor.values()    #得到兩個list
['python', 'qiwsir', 'qiwsir.github.io']
>>> myinfor.keys()
['lang', 'name', 'site']
>>> temp = zip(myinfor.values(),myinfor.keys())     #壓縮成一個list,每個元素是一個tuple
>>> temp
[('python', 'lang'), ('qiwsir', 'name'), ('qiwsir.github.io', 'site')]

>>> dict(temp)                          #這是函數dict()的功能,將上述列表轉化為dictionary
{'python': 'lang', 'qiwsir.github.io': 'site', 'qiwsir': 'name'}

至此,是不是明白zip()和循環(huán)的關系了呢?有了它可以讓某些循環(huán)簡化。

enumerate

這是一個有意思的內置函數,本來我們可以通過for i in range(len(list))的方式得到一個list的每個元素索引,然后在用list[i]的方式得到該元素。如果要同時得到元素索引和元素怎么辦?就是這樣了:

>>> for i in range(len(week)):
...     print week[i]+' is '+str(i)     #注意,i是int類型,如果和前面的用+連接,必須是str類型
... 
monday is 0
sunday is 1
friday is 2

python中提供了一個內置函數enumerate,能夠實現類似的功能

>>> for (i,day) in enumerate(week):
...     print day+' is '+str(i)
... 
monday is 0
sunday is 1
friday is 2

官方文檔是這么說的:

Return an enumerate object. sequence must be a sequence, an iterator, or some other object which supports iteration. The next() method of the iterator returned by enumerate() returns a tuple containing a count (from start which defaults to 0) and the values obtained from iterating over sequence:

順便抄錄幾個例子,供看官欣賞,最好實驗一下。

>>> seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
>>> list(enumerate(seasons, start=1))
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

對于這樣一個列表:

>>> mylist = ["qiwsir",703,"python"]
>>> enumerate(mylist)
<enumerate object at 0xb74a63c4>    

出現這個結果,用list就能實現轉換,顯示內容.意味著可迭代。

>>> list(enumerate(mylist))
[(0, 'qiwsir'), (1, 703), (2, 'python')]

再設計一個小問題,練習一下這個函數。

問題:將字符串中的某些字符替換為其它的字符串。原始字符串"Do you love Canglaoshi? Canglaoshi is a good teacher.",請將"Canglaoshi"替換為"PHP".

解析:

>>> raw = "Do you love Canglaoshi? Canglaoshi is a good teacher."

這是所要求的那個字符串,當時,不能直接對這個字符串使用enumerate(),因為它會變成這樣:

>>> list(enumerate(raw))
[(0, 'D'), (1, 'o'), (2, ' '), (3, 'y'), (4, 'o'), (5, 'u'), (6, ' '), (7, 'l'), (8, 'o'), (9, 'v'), (10, 'e'), (11, ' '), (12, 'C'), (13, 'a'), (14, 'n'), (15, 'g'), (16, 'l'), (17, 'a'), (18, 'o'), (19, 's'), (20, 'h'), (21, 'i'), (22, '?'), (23, ' '), (24, 'C'), (25, 'a'), (26, 'n'), (27, 'g'), (28, 'l'), (29, 'a'), (30, 'o'), (31, 's'), (32, 'h'), (33, 'i'), (34, ' '), (35, 'i'), (36, 's'), (37, ' '), (38, 'a'), (39, ' '), (40, 'g'), (41, 'o'), (42, 'o'), (43, 'd'), (44, ' '), (45, 't'), (46, 'e'), (47, 'a'), (48, 'c'), (49, 'h'), (50, 'e'), (51, 'r'), (52, '.')]

這不是所需要的。所以,先把raw轉化為列表:

>>> raw_lst = raw.split(" ")

然后用enumerate()

>>> for i, string in enumerate(raw_lst):
...     if string == "Canglaoshi":
...         raw_lst[i] = "PHP"
... 

沒有什么異常現象,查看一下那個raw_lst列表,看看是不是把"Canglaoshi"替換為"PHP"了。

>>> raw_lst
['Do', 'you', 'love', 'Canglaoshi?', 'PHP', 'is', 'a', 'good', 'teacher.']

只替換了一個,還有一個沒有替換。為什么?仔細觀察發(fā)現,沒有替換的那個是'Canglaoshi?',跟條件判斷中的"Canglaoshi"不一樣。

修改一下,把條件放寬:

>>> for i, string in enumerate(raw_lst):
...     if "Canglaoshi" in string:
...         raw_lst[i] = "PHP"
... 
>>> raw_lst
['Do', 'you', 'love', 'PHP', 'PHP', 'is', 'a', 'good', 'teacher.']

好的。然后呢?再轉化為字符串?留給讀者試試。

list解析

先看下面的例子,這個例子是想得到1到9的每個整數的平方,并且將結果放在list中打印出來

>>> power2 = []
>>> for i in range(1,10):
...     power2.append(i*i)
... 
>>> power2
[1, 4, 9, 16, 25, 36, 49, 64, 81]

python有一個非常有意思的功能,就是list解析,就是這樣的:

>>> squares = [x**2 for x in range(1,10)]
>>> squares
[1, 4, 9, 16, 25, 36, 49, 64, 81]

看到這個結果,看官還不驚嘆嗎?這就是python,追求簡潔優(yōu)雅的python!

其官方文檔中有這樣一段描述,道出了list解析的真諦:

List comprehensions provide a concise way to create lists. Common applications are to make new lists where each element is the result of some operations applied to each member of another sequence or iterable, or to create a subsequence of those elements that satisfy a certain condition.

這就是python有意思的地方,也是計算機高級語言編程有意思的地方,你只要動腦筋,總能找到驚喜的東西。

其實,不僅僅對數字組成的list,所有的都可以如此操作。請在平復了激動的心之后,默默地看下面的代碼,感悟一下list解析的魅力。

>>> mybag = [' glass',' apple','green leaf ']   #有的前面有空格,有的后面有空格
>>> [one.strip() for one in mybag]              #去掉元素前后的空格
['glass', 'apple', 'green leaf']

上面的問題,都能用list解析來重寫。讀者不妨試試。

在很多情況下,list解析的執(zhí)行效率高,代碼簡潔明了。是實際寫程序中經常被用到的。

以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號