編寫循環(huán)的技巧,for循環(huán)包含了常見的計數(shù)器式循環(huán)。由于通常for循環(huán)比while循環(huán)更容易寫,也執(zhí)行得更快,所以for循環(huán)一般是你遍歷序列或其他可迭代對象時的首選。事實上,作為一條通用法則,你應該克制在Python中使用計數(shù)方式的誘惑——Python提供的迭代工具,能幫你把像C這樣低級語言中循環(huán)集合體的工作自動化。不過,有些情況下你還是需要以更為特定的方式進行迭代。例如,如果你需要在列表中每隔一個元素或每隔兩個元素進行訪問,或是要同時修改列表呢?如果在同一個for循環(huán)內(nèi),并行遍歷一個以上的序列呢?如果你也需要進行索引呢?
創(chuàng)新互聯(lián)公司是一家專注于成都做網(wǎng)站、成都網(wǎng)站建設與策劃設計,松江網(wǎng)站建設哪家好?創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設10年,網(wǎng)設計領域的專業(yè)建站公司;建站業(yè)務涵蓋:松江等地區(qū)。松江做網(wǎng)站價格咨詢:13518219792
你總是可以用while循環(huán)和手動索引運算來編寫這些獨特的循環(huán),但是python提供了一套內(nèi)置函數(shù),可以幫你在for循環(huán)內(nèi)定制迭代:·內(nèi)置函數(shù)range (Python 0.X及之后版本可用)返回一系列連續(xù)增加的整數(shù),可作為for中的索引。內(nèi)置函數(shù)zip (Python 2.0及之后版本可用)返回一系列并行元素的元組,可用于在for中內(nèi)遍歷多個序列。內(nèi)置函數(shù)enumerate (Python 2.3及之后版本可用)同時生成可迭代對象中元素的值和索引,因而我們不必再手動計數(shù)。內(nèi)置函數(shù)map (Python 1.0及之后版本可用)在Python 2.X中與zip有著相似的效果,但是在3.X中 map 的這一角色被移除了。因為for循環(huán)可能會比基于while的計數(shù)器循環(huán)運行得更快,所以借助這些工具并盡可能地使用for循環(huán),會讓你受益匪淺。讓我們在常見的使用場景下,依次看一看這些內(nèi)置函數(shù)吧。我們將會看到,它們的用法在Python 2.X和3.X中稍有不同,同時它們中的一些要比其他的更加有效。
循環(huán)是一種常用的程序控制結(jié)構。我們常說,機器相比人類的最大優(yōu)點之一,就是機器可以不眠不休的重復做某件事情,但人卻不行。而“循環(huán)”,則是實現(xiàn)讓機器不斷重復工作的關鍵概念。
在循環(huán)語法方面,Python 表現(xiàn)的即傳統(tǒng)又不傳統(tǒng)。它雖然拋棄了常見的 for(init;condition;incrment) 三段式結(jié)構,但還是選擇了 for 和 while 這兩個經(jīng)典的關鍵字來表達循環(huán)。絕大多數(shù)情況下,我們的循環(huán)需求都可以用 forin來滿足, while相比之下用的則更少些。
雖然循環(huán)的語法很簡單,但是要寫好它確并不容易。在這篇文章里,我們將探討什么是“地道”的循環(huán)代碼,以及如何編寫它們。
什么是“地道”的循環(huán)?
“地道”這個詞,通常被用來形容某人做某件事情時,非常符合當?shù)貍鹘y(tǒng),做的非常好。打個比方,你去參加一個朋友聚會,同桌的有一位廣東人,對方一開口,句句都是標準京腔、完美兒化音。那你可以對她說:“您的北京話說的真地道”。
既然“地道”這個詞形容的經(jīng)常是口音、做菜的口味這類實實在在的東西,那“地道”的循環(huán)代碼又是什么意思呢?讓我拿一個經(jīng)典的例子來解釋一下。
如果你去問一位剛學習 Python 一個月的人:“如何在遍歷一個列表的同時獲取當前下標?”。他可能會交出這樣的代碼:
?
上面的循環(huán)雖然沒錯,但它確一點都不“地道”。一個擁有三年 Python 開發(fā)經(jīng)驗的人會說,代碼應該這么寫:
enumerate() 是 Python 的一個內(nèi)置函數(shù),它接收一個“可迭代”對象作為參數(shù),然后返回一個不斷生成 (當前下標,當前元素) 的新可迭代對象。這個場景使用它最適合不過。
所以,在上面的例子里,我們會認為第二段循環(huán)代碼比第一段更“地道”。因為它用更直觀的代碼,更聰明的完成了工作。
enumerate() 所代表的編程思路
不過,判斷某段循環(huán)代碼是否地道,并不僅僅是以知道或不知道某個內(nèi)置方法作為標準。我們可以從上面的例子挖掘出更深層的東西。
如你所見,Python 的 for 循環(huán)只有 forin這一種結(jié)構,而結(jié)構里的前半部分 - 賦值給 item- 沒有太多花樣可玩。所以后半部分的 可迭代對象 是我們唯一能夠大做文章的東西。而以 enumerate() 函數(shù)為代表的“修飾函數(shù)”,剛好提供了一種思路:通過修飾可迭代對象來優(yōu)化循環(huán)本身。
這就引出了我的第一個建議。
建議1:使用函數(shù)修飾被迭代對象來優(yōu)化循環(huán)
使用修飾函數(shù)處理可迭代對象,可以在各種方面影響循環(huán)代碼。而要找到合適的例子來演示這個方法,并不用去太遠,內(nèi)置模塊 itertools 就是一個絕佳的例子。
簡單來說,itertools 是一個包含很多面向可迭代對象的工具函數(shù)集。我在之前的系列文章《容器的門道》里提到過它。
如果要學習 itertools,那么 Python 官方文檔 是你的首選,里面有非常詳細的模塊相關資料。但在這篇文章里,側(cè)重點將和官方文檔稍有不同。我會通過一些常見的代碼場景,來詳細解釋它是如何改善循環(huán)代碼的。
1. 使用 product 扁平化多層嵌套循環(huán)
雖然我們都知道“扁平的代碼比嵌套的好”。但有時針對某類需求,似乎一定得寫多層嵌套循環(huán)才行。比如下面這段:
?
對于這種需要嵌套遍歷多個對象的多層循環(huán)代碼,我們可以使用 product() 函數(shù)來優(yōu)化它。product() 可以接收多個可迭代對象,然后根據(jù)它們的笛卡爾積不斷生成結(jié)果。
?
相比之前的代碼,使用 product() 的函數(shù)只用了一層 for 循環(huán)就完成了任務,代碼變得更精煉了。
2. 使用 islice 實現(xiàn)循環(huán)內(nèi)隔行處理
有一份包含 Reddit 帖子標題的外部數(shù)據(jù)文件,里面的內(nèi)容格式是這樣的:
?
可能是為了美觀,在這份文件里的每兩個標題之間,都有一個 "---" 分隔符?,F(xiàn)在,我們需要獲取文件里所有的標題列表,所以在遍歷文件內(nèi)容的過程中,必須跳過這些無意義的分隔符。
參考之前對 enumerate() 函數(shù)的了解,我們可以通過在循環(huán)內(nèi)加一段基于當前循環(huán)序號的 if 判斷來做到這一點:
?
但對于這類在循環(huán)內(nèi)進行隔行處理的需求來說,如果使用 itertools 里的 islice() 函數(shù)修飾被循環(huán)對象,可以讓循環(huán)體代碼變得更簡單直接。
islice(seq,start,end,step) 函數(shù)和數(shù)組切片操作( list[start:stop:step] )有著幾乎一模一樣的參數(shù)。如果需要在循環(huán)內(nèi)部進行隔行處理的話,只要設置第三個遞進步長參數(shù) step 值為 2 即可(默認為 1)。
?
3. 使用 takewhile 替代 break 語句
有時,我們需要在每次循環(huán)開始時,判斷循環(huán)是否需要提前結(jié)束。比如下面這樣:
?
對于這類需要提前中斷的循環(huán),我們可以使用 takewhile() 函數(shù)來簡化它。takewhile(predicate,iterable)會在迭代 iterable 的過程中不斷使用當前對象作為參數(shù)調(diào)用 predicate 函數(shù)并測試返回結(jié)果,如果函數(shù)返回值為真,則生成當前對象,循環(huán)繼續(xù)。否則立即中斷當前循環(huán)。
使用 takewhile 的代碼樣例:
?
itertools 里面還有一些其他有意思的工具函數(shù),他們都可以用來和循環(huán)搭配使用,比如使用 chain 函數(shù)扁平化雙層嵌套循環(huán)、使用 zip_longest 函數(shù)一次同時循環(huán)多個對象等等。
篇幅有限,我在這里不再一一介紹。如果有興趣,可以自行去官方文檔詳細了解。
4. 使用生成器編寫自己的修飾函數(shù)
除了 itertools 提供的那些函數(shù)外,我們還可以非常方便的使用生成器來定義自己的循環(huán)修飾函數(shù)。
讓我們拿一個簡單的函數(shù)舉例:
?
在上面的函數(shù)里,循環(huán)體內(nèi)為了過濾掉所有奇數(shù),引入了一條額外的 if 判斷語句。如果要簡化循環(huán)體內(nèi)容,我們可以定義一個生成器函數(shù)來專門進行偶數(shù)過濾:
?
將 numbers 變量使用 even_only 函數(shù)裝飾后, sum_even_only_v2 函數(shù)內(nèi)部便不用繼續(xù)關注“偶數(shù)過濾”邏輯了,只需要簡單完成求和即可。
Hint:當然,上面的這個函數(shù)其實并不實用。在現(xiàn)實世界里,這種簡單需求最適合直接用生成器/列表表達式搞定:sum(numfornuminnumbersifnum%2==0)
建議2:按職責拆解循環(huán)體內(nèi)復雜代碼塊
我一直覺得循環(huán)是一個比較神奇的東西,每當你寫下一個新的循環(huán)代碼塊,就好像開辟了一片黑魔法陣,陣內(nèi)的所有內(nèi)容都會開始無休止的重復執(zhí)行。
但我同時發(fā)現(xiàn),這片黑魔法陣除了能帶來好處,它還會引誘你不斷往陣內(nèi)塞入越來越多的代碼,包括過濾掉無效元素、預處理數(shù)據(jù)、打印日志等等。甚至一些原本不屬于同一抽象的內(nèi)容,也會被塞入到同一片黑魔法陣內(nèi)。
想要實現(xiàn)從 0 到 10 的累加,可以執(zhí)行如下代碼:
print("計算 0+2+...+10 的結(jié)果為:")
#保存累加結(jié)果的變量result = 0
#逐個獲取從 0到 10 這些值,并做累加操作
for i in range(0,10):
result += iprint(result)
上面代碼中,使用了 range() 函數(shù),此函數(shù)是 Python 內(nèi)置函數(shù),用于生成一系列連續(xù)整數(shù),多用于 for 循環(huán)中。
擴展資料:
使用Python編程時的注意事項:
1、初始變化量
在Python里,一個表達式中的名字在它被賦值之前是沒法使用的。這是有意而為的:這樣能避免一些輸入失誤,同時也能避免默認究竟應該是什么類型的問題(0,None,””,[],?)。記住把計數(shù)器初始化為0,列表初始化為[],以此類推。
2、從第一列開始
確保把頂層的,未嵌套的代碼放在最左邊第一列開始。這包括在模塊文件中未嵌套的代碼,以及在交互提示符中未嵌套的代碼。Python使用縮進的辦法來區(qū)分嵌套的代碼段,因此在你代碼左邊的空格意味著嵌套的代碼塊。除了縮進以外,空格通常是被忽略掉的。