for循環(huán)在python中應(yīng)用廣泛,所以,要用更多的篇幅來(lái)介紹。
關(guān)于迭代,在《列表(2)》中曾經(jīng)提到過(guò)“可迭代的(iterable)”這個(gè)詞,并給予了適當(dāng)解釋,這里再次提到“迭代”,說(shuō)明它在python中占有重要的位置。
迭代,在python中表現(xiàn)就是用for循環(huán),從序列對(duì)象中獲得一定數(shù)量的元素。
在前面一節(jié)中,用for循環(huán)來(lái)獲得列表、字符串、元組,乃至于字典的鍵值對(duì),都是迭代。
現(xiàn)實(shí)中迭代不都是那么簡(jiǎn)單的,比如這個(gè)問(wèn)題:
問(wèn)題:有兩個(gè)列表,分別是:a = [1,2,3,4,5], b = [9,8,7,6,5],要計(jì)算這兩個(gè)列表中對(duì)應(yīng)元素的和。
解析:
太簡(jiǎn)單了,一看就知道結(jié)果了。
很好,這是你的方法,如果是computer姑娘來(lái)做,應(yīng)該怎么做呢?
觀察發(fā)現(xiàn)兩個(gè)列表的長(zhǎng)度一樣,都是5。那么對(duì)應(yīng)元素求和,就是相同的索引值對(duì)應(yīng)的元素求和,即a[i]+b[i],(i=0,1,2,3,4),這樣一個(gè)一個(gè)地就把相應(yīng)元素和求出來(lái)了。當(dāng)然,要用for來(lái)做這個(gè)事情了。
>>> 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]
看來(lái)for的表現(xiàn)還不錯(cuò)。不過(guò),這種方法雖然解決問(wèn)題了,但python總不會(huì)局限于一個(gè)解決之道。于是又有一個(gè)內(nèi)建函數(shù)zip()
,可以讓同樣的問(wèn)題有不一樣的解決途徑。
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分別代表了序列類型的數(shù)據(jù)。通過(guò)實(shí)驗(yàn)來(lái)理解上面的文檔:
>>> a = "qiwsir"
>>> b = "github"
>>> zip(a,b)
[('q', 'g'), ('i', 'i'), ('w', 't'), ('s', 'h'), ('i', 'u'), ('r', 'b')]
如果序列長(zhǎng)度不同,那么就以"the length of the shortest argument sequence"為準(zhǔn)。
>>> 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是字典嗎?當(dāng)然不是。下面的才是字典呢。
>>> s = {"name":"qiwsir"}
>>> t = {"lang":"python"}
>>> zip(s,t)
[('name', 'lang')]
zip是一個(gè)內(nèi)置函數(shù),它的參數(shù)必須是某種序列數(shù)據(jù)類型,如果是字典,那么鍵視為序列。然后將序列對(duì)應(yīng)的元素依次組成元組,做為一個(gè)list的元素。
下面是比較特殊的情況,參數(shù)是一個(gè)序列數(shù)據(jù)的時(shí)候,生成的結(jié)果樣子:
>>> a
'qiwsir'
>>> c
[1, 2, 3]
>>> zip(c)
[(1,), (2,), (3,)]
>>> zip(a)
[('q',), ('i',), ('w',), ('s',), ('i',), ('r',)]
很好的zip()
!那么就用它來(lái)解決前面那個(gè)兩個(gè)列表中值對(duì)應(yīng)相加吧。
>>> d = []
>>> for x,y in zip(a,b):
... d.append(x+y)
...
>>> d
[10, 10, 10, 10, 10]
多么優(yōu)雅的解決!
比較這個(gè)問(wèn)題的兩種解法,似乎第一種解法適用面較窄,比如,如果已知給定的兩個(gè)列表長(zhǎng)度不同,第一種解法就出問(wèn)題了。而第二種解法還可以繼續(xù)適用。的確如此,不過(guò),第一種解法也不是不能修訂的。
>>> a = [1,2,3,4,5]
>>> b = ["python","www.itdiffer.com","qiwsir"]
如果已知是這樣兩個(gè)列表,要講對(duì)應(yīng)的元素“加起來(lái)”。
>>> length = len(a) if len(a)<len(b) else len(b)
>>> length
3
首先用這種方法獲得兩個(gè)列表中最短的那個(gè)列表的長(zhǎng)度??茨蔷淙僮?,這是非常pythonic的寫(xiě)法啦。寫(xiě)出這句,就可以冒充高手了。哈哈。
>>> for i in range(length):
... c.append(str(a[i]) + ":" + b[i])
...
>>> c
['1:python', '2:www.itdiffer.com', '3:qiwsir']
我還是用第一個(gè)思路做的,經(jīng)過(guò)這么修正一下,也還能用。要注意一個(gè)細(xì)節(jié),在“加”的時(shí)候,不能直接用a[i]
,因?yàn)樗玫膶?duì)象是一個(gè)int類型,不能跟后面的str類型相加,必須轉(zhuǎn)化一下。
當(dāng)然,zip()
也是能解決這個(gè)問(wèn)題的。
>>> 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'
報(bào)錯(cuò)!看錯(cuò)誤信息,我剛剛提醒的那個(gè)問(wèn)題就冒出來(lái)了。所以,應(yīng)該這么做:
>>> for x,y in zip(a,b):
... d.append(str(x) + ":" + y)
...
>>> d
['1:python', '2:www.itdiffer.com', '3:qiwsir']
這才得到了正確結(jié)果。
切記:computer是一個(gè)姑娘,她非常秀氣,需要敲代碼的小伙子們耐心地、細(xì)心地跟她相處。
以上兩種寫(xiě)法那個(gè)更好呢?前者?后者?哈哈。我看差不多了。
>>> result
[(2, 11), (4, 13), (6, 15), (8, 17)]
>>> zip(*result)
[(2, 4, 6, 8), (11, 13, 15, 17)]
zip()
還能這么干,是不是有點(diǎn)意思?
下面延伸一個(gè)問(wèn)題:
問(wèn)題:有一個(gè)dictionary,myinfor = {"name":"qiwsir","site":"qiwsir.github.io","lang":"python"},將這個(gè)字典變換成:infor = {"qiwsir":"name","qiwsir.github.io":"site","python":"lang"}
解析:
解法有幾個(gè),如果用for循環(huán),可以這樣做(當(dāng)然,看官如果有方法,歡迎貼出來(lái))。
>>> infor = {}
>>> for k,v in myinfor.items():
... infor[v]=k
...
>>> infor
{'python': 'lang', 'qiwsir.github.io': 'site', 'qiwsir': 'name'}
下面用zip()來(lái)試試:
>>> dict(zip(myinfor.values(),myinfor.keys()))
{'python': 'lang', 'qiwsir.github.io': 'site', 'qiwsir': 'name'}
嗚呼,這是什么情況?原來(lái)這個(gè)zip()還能這樣用。是的,本質(zhì)上是這么回事情。如果將上面這一行分解開(kāi)來(lái),看官就明白其中的奧妙了。
>>> myinfor.values() #得到兩個(gè)list
['python', 'qiwsir', 'qiwsir.github.io']
>>> myinfor.keys()
['lang', 'name', 'site']
>>> temp = zip(myinfor.values(),myinfor.keys()) #壓縮成一個(gè)list,每個(gè)元素是一個(gè)tuple
>>> temp
[('python', 'lang'), ('qiwsir', 'name'), ('qiwsir.github.io', 'site')]
>>> dict(temp) #這是函數(shù)dict()的功能,將上述列表轉(zhuǎn)化為dictionary
{'python': 'lang', 'qiwsir.github.io': 'site', 'qiwsir': 'name'}
至此,是不是明白zip()和循環(huán)的關(guān)系了呢?有了它可以讓某些循環(huán)簡(jiǎn)化。
這是一個(gè)有意思的內(nèi)置函數(shù),本來(lái)我們可以通過(guò)for i in range(len(list))
的方式得到一個(gè)list的每個(gè)元素索引,然后在用list[i]的方式得到該元素。如果要同時(shí)得到元素索引和元素怎么辦?就是這樣了:
>>> 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中提供了一個(gè)內(nèi)置函數(shù)enumerate,能夠?qū)崿F(xiàn)類似的功能
>>> for (i,day) in enumerate(week):
... print day+' is '+str(i)
...
monday is 0
sunday is 1
friday is 2
官方文檔是這么說(shuō)的:
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:
順便抄錄幾個(gè)例子,供看官欣賞,最好實(shí)驗(yàn)一下。
>>> 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')]
對(duì)于這樣一個(gè)列表:
>>> mylist = ["qiwsir",703,"python"]
>>> enumerate(mylist)
<enumerate object at 0xb74a63c4>
出現(xiàn)這個(gè)結(jié)果,用list就能實(shí)現(xiàn)轉(zhuǎn)換,顯示內(nèi)容.意味著可迭代。
>>> list(enumerate(mylist))
[(0, 'qiwsir'), (1, 703), (2, 'python')]
再設(shè)計(jì)一個(gè)小問(wèn)題,練習(xí)一下這個(gè)函數(shù)。
問(wèn)題:將字符串中的某些字符替換為其它的字符串。原始字符串"Do you love Canglaoshi? Canglaoshi is a good teacher.",請(qǐng)將"Canglaoshi"替換為"PHP".
解析:
>>> raw = "Do you love Canglaoshi? Canglaoshi is a good teacher."
這是所要求的那個(gè)字符串,當(dāng)時(shí),不能直接對(duì)這個(gè)字符串使用enumerate()
,因?yàn)樗鼤?huì)變成這樣:
>>> 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轉(zhuǎn)化為列表:
>>> raw_lst = raw.split(" ")
然后用enumerate()
>>> for i, string in enumerate(raw_lst):
... if string == "Canglaoshi":
... raw_lst[i] = "PHP"
...
沒(méi)有什么異?,F(xiàn)象,查看一下那個(gè)raw_lst列表,看看是不是把"Canglaoshi"替換為"PHP"了。
>>> raw_lst
['Do', 'you', 'love', 'Canglaoshi?', 'PHP', 'is', 'a', 'good', 'teacher.']
只替換了一個(gè),還有一個(gè)沒(méi)有替換。為什么?仔細(xì)觀察發(fā)現(xiàn),沒(méi)有替換的那個(gè)是'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.']
好的。然后呢?再轉(zhuǎn)化為字符串?留給讀者試試。
先看下面的例子,這個(gè)例子是想得到1到9的每個(gè)整數(shù)的平方,并且將結(jié)果放在list中打印出來(lái)
>>> power2 = []
>>> for i in range(1,10):
... power2.append(i*i)
...
>>> power2
[1, 4, 9, 16, 25, 36, 49, 64, 81]
python有一個(gè)非常有意思的功能,就是list解析,就是這樣的:
>>> squares = [x**2 for x in range(1,10)]
>>> squares
[1, 4, 9, 16, 25, 36, 49, 64, 81]
看到這個(gè)結(jié)果,看官還不驚嘆嗎?這就是python,追求簡(jiǎn)潔優(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有意思的地方,也是計(jì)算機(jī)高級(jí)語(yǔ)言編程有意思的地方,你只要?jiǎng)幽X筋,總能找到驚喜的東西。
其實(shí),不僅僅對(duì)數(shù)字組成的list,所有的都可以如此操作。請(qǐng)?jiān)谄綇?fù)了激動(dòng)的心之后,默默地看下面的代碼,感悟一下list解析的魅力。
>>> mybag = [' glass',' apple','green leaf '] #有的前面有空格,有的后面有空格
>>> [one.strip() for one in mybag] #去掉元素前后的空格
['glass', 'apple', 'green leaf']
上面的問(wèn)題,都能用list解析來(lái)重寫(xiě)。讀者不妨試試。
在很多情況下,list解析的執(zhí)行效率高,代碼簡(jiǎn)潔明了。是實(shí)際寫(xiě)程序中經(jīng)常被用到的。
更多建議: