一、簡介
Python的條件和迴圈語句,決定了程式的控制流程程,體現結構的多樣性。須重要理解,if、while、for以及與它們相搭配的 else、 elif、break、continue和pass語句。
二、詳解
1、if語句
Python中的if子句由三部分組成:關鍵字本身、用於判斷結果真假的條件運算式以及當運算式為真或者非零時執行的代碼塊。if 語句的文法如下:
if expression: expr_true_suite
if 語句的expr_true_suite代碼塊只有在條件運算式的結果的布爾值為真時才執行,否則將繼續執行緊跟在該代碼塊後面的語句。
(1)多重條件運算式
單個if語句可以通過使用布爾操作符and、or和not,實現多重判斷條件或是否定判斷條件。
(2)單一語句的代碼塊
如果一個複合陳述式(例如if子句、while或for迴圈)的代碼塊僅僅包含一行代碼,那麼它可以和前面的語句寫在同一行上。如if make_hard_copy: send_data_to_printer(),這樣的單行語句是合法的, 儘管它可能方便,但這樣會使得代碼更難閱讀, 所以推薦將這行代碼移到下一行併合理地縮排。另外一個原因就是如果你需要添加新的代碼, 你還是得把它移到下一行。
2、else語句
Python提供了與if語句搭配使用的else語句,如果if語句的條件運算式的結果布爾值為假,那麼程式將執行 else 語句後的代碼。其文法如下:
if expression: expr_true_suiteelse: expr_false_suite
在C語言中,不會在條件陳述式範圍外發現else語句, 但Python不同,可以在while和for迴圈中使用else語句,在迴圈中使用時,else子句只在迴圈完成後執行,也就是說break語句也會跳過else塊。
例:顯示出10到20中的數位最大約數
在CODE上查看代碼片派生到My Code片
#!/usr/bin/env python def showMaxFactor(num): count = num / 2 while count > 1: if (num % count == 0): print 'largest factor of %d is %d' % (num, count) break count = count - 1 else: print eachNum, 'is prime' for eachNum in range(10, 21): showMaxFactor(eachNum)
在CODE上查看代碼片派生到My Code片
largest factor of 10 is 5 11 is prime largest factor of 12 is 6 13 is prime largest factor of 14 is 7 largest factor of 15 is 5 largest factor of 16 is 8 17 is prime largest factor of 18 is 9 19 is prime largest factor of 20 is 10
3、elif (即else-if )語句
elif是Python的else-if 語句,它檢查多個運算式是否為真,並在為真時執行特定代碼塊中的代碼。和else一樣,elif 聲明是可選的,然而不同的是if 語句後最多隻能有一個else語句,但可以有任意數量的 elif 語句。
if expression1: expr1_true_suiteelif expression2: expr2_true_suite ...elif expressionN: exprN_true_suiteelse: none_of_the_above_suite
在將來的某天,Python可能會支援 switch /case語句,但是完全可以用其他的Python結構來類比它。在Python中,大量的if-elif 語句並不難閱讀。
在CODE上查看代碼片派生到My Code片
if user.cmd == 'create': action = "create item" elif user.cmd == 'delete': action = 'delete item' elif user.cmd == 'update': action = 'update item' else: action = 'invalid choice... try again!'
上面的語句還可以用序列和成員關係操作符來簡化它:
在CODE上查看代碼片派生到My Code片
if user.cmd in ('create', 'delete', 'update'): action = '%s item' % user.cmd else: action = 'invalid choice... try again!'
還可以用Python字典給出更加優雅的解決方案,使用映射對象(比如字典)的一個最大好處就是它的搜尋操作比類似語句或是 for 迴圈這樣的序列查詢要快很多。
在CODE上查看代碼片派生到My Code片
msgs = {'create': 'create item', 'delete': 'delete item', 'update': 'update item' } default = 'invalid choice... try again!' action = msgs.get(user.cmd, default)
4、條件運算式(即"三元操作符")
三元運算子文法為:X if C else Y,只需要一行完成條件判斷和賦值操作:
在CODE上查看代碼片派生到My Code片
>>> x, y = 4, 3 >>> smaller = x if x < y else y >>> smaller 3
5、while語句
while是一個條件迴圈語句,與if聲明相比,如果 if 後的條件為真,就會執行一次相應的代碼塊。而while中的代碼塊會一直迴圈執行,直到迴圈條件不再為真。
(1)一般文法
while迴圈的文法如下:
while expression: suite_to_repeat
while迴圈的suite_to_repeat子句會一直迴圈執行,直到expression值為布爾假。
(2)計數迴圈
count = 0while (count < 9): print 'the index is:', count count += 1
代碼塊裡包含了print和自增語句,它們被重複執行,直到count不再小於9。索引count在每次迭代時被列印出來然後自增 1。
(3)無限迴圈
while True: handle, indata = wait_for_client_connect() outdata = process_request(indata) ack_result_to_client(handle, outdata)
“無限”迴圈永遠不會結束,但它不一定是壞事,許多通訊伺服器的用戶端/伺服器系統就是通過它來工作的。
6、for語句
Python提供了的另一個迴圈機制就是for語句,它是Python中最強大的迴圈結構。它可以遍曆序列成員,可以用在列表解析和產生器運算式中,它會自動地調用迭代器的next()方法,捕獲StopIteration異常並結束迴圈(所有這一切都是在內部發生的)。 Python的for更像是shell或是指令碼語言中的foreach迴圈。
(1)一般文法
for迴圈會訪問一個可迭代對象(例如序列或是迭代器)中的所有元素,,並在所有條目都處理過後結束迴圈。它的文法如下:
for iter_var in iterable:
suite_to_repeat
每次迴圈, iter_var迭代變數被設定為可迭代對象(序列、迭代器或者是其他支援迭代的對象)的當前元素,提供給suite_to_repeat 語句塊使用。
(2)用於序列類型
for迴圈可以迭代不同的序列對象,像字串、 列表、以及元組。
迭代序列有三種基本方法:
通過序列項迭代
在CODE上查看代碼片派生到My Code片
>>> nameList = ['Walter', "Nicole", 'Steven', 'Henry'] >>> for eachName in nameList: ... print eachName, "Lim" ... Walter Lim Nicole Lim Steven Lim Henry Lim
迭代一個列表.。每次迭代,eacgName變數都被設定為列表中特定某個元素。
通過序列索引迭代
在CODE上查看代碼片派生到My Code片
>>> nameList = ['Cathy', "Terry", 'Joe', 'Heather','Lucy'] >>> for nameIndex in range(len(nameList)): ... print "Liu,", nameList[nameIndex] ... Liu, Cathy Liu, Terry Liu, Joe Liu, Heather Liu, Lucy
沒有迭代元素, 而是通過列表的索引迭代。但通過直接迭代序列要比索引迭代快。
使用項和索引迭代
在CODE上查看代碼片派生到My Code片
>>> nameList = ['Donn', 'Shirley', 'Ben', 'Janice','David', 'Yen', 'Wendy'] >>> for i, eachLee in enumerate(nameList): ... print "%d %s Lee" % (i+1, eachLee) ... 1 Donn Lee 2 Shirley Lee 3 Ben Lee 4 Janice Lee 5 David Lee 6 Yen Lee 7 Wendy Lee
(3)用於迭代器類型
用for逐一查看迭代器和訪問序列的方法差不多,迭代器並不代表迴圈條目的集合,迭代器對象有一個next()方法, 調用後返回下一個條目。 所有條目迭代完後, 迭代器引發一個StopIteration異常告訴程式迴圈結束,for語句在內部調用next()並捕獲異常。
使用迭代器做 for 迴圈的代碼與使用序列條目幾乎完全相同。事實上在大多情況下,無法分辨出迭代的是一個序列還是迭代器,因此遍曆一個迭代器時,實際上可能指的是要遍曆一個序列、迭代器或是一個支援迭代的對象(它有 next()方法)。
(4)range()內建函數
內建函數range()可以把類似foreach的for迴圈變成你更加熟悉的語句。
Python提供了兩種不同的方法來調用range() ,完整文法要求提供兩個或三個整數參數:range(start, end, step =1),range()會返回一個包含所有k的列表,其中start <= k < end,從start到end ,k每次遞增 ste,step不可以為零,否則將發生錯誤。
在CODE上查看代碼片派生到My Code片
>>> range(3, 7) [3, 4, 5, 6] >>> for eachVal in range(2, 19, 3): ... print "value is:", eachVal ... value is: 2 value is: 5 value is: 8 value is: 11 value is: 14 value is: 17
range() 還有兩種簡略的文法格式:range(end)和range(start, end)。 start 預設為0, step預設為1。
(5)xrange()內建函數
xrange()類似range(),不過當有一個很大的範圍列表時,xrange() 可能更為適合, 因為它不會在記憶體裡建立列表的完整拷貝。 它只被用在 for 迴圈中,在 for 迴圈外使用它沒有意義。它的效能遠高出 range(),因為它不產生整個列表。在Python的將來版本中,range()可能會像xrange()一樣,返回一個可迭代對象(不是列表也不是一個迭代器)。
(6)與序列相關的內建函數
序列相關函數:sorted()、 reversed()、enumerate()和zip(),稱為“序列相關”是因為其中兩個函數(sorted() 和 zip())返回一個序列(列表),而另外兩個函數(reversed() 和 enumerate())返回迭代器(類似序列)。
7、break和continue語句
Python中的break語句可以結束當前迴圈然後跳轉到下條語句,類似C中的break。常用在當某個外部條件被觸發(一般通過 if 語句檢查),需要立即從迴圈中退出時.。break 語句可以用在 while 和 for 迴圈中。
Python裡的continue語句和其他進階語言中的傳統continue並沒有什麼不同,它可以被用在while和for迴圈裡。 while迴圈是條件
性的,而 for 迴圈是迭代的,所以continue在開始下一次迴圈前要滿足一些先決條件,否則迴圈會正常結束。
程式中當遇到 continue 語句時, 程式會終止當前迴圈,並忽略剩餘的語句,然後回到迴圈的頂端。在開始下一次迭代前,如果是條件迴圈,我們將驗證條件運算式。如果是迭代迴圈,將驗證是否還有元素可以迭代。只有在驗證成功的情況下,才會開始下一次迭代。
在CODE上查看代碼片派生到My Code片
#!/usr/bin/env python valid = False count = 3 passwdList=('abc',) while count > 0 and valid == False: input = raw_input("enter password:").strip() # check for valid passwd for eachPasswd in passwdList: if input == eachPasswd: valid = True break if not valid: # (or valid == 0) print "invalid input" count -= 1 continue else: break
結合使用了while、for、if、break以及continue,來驗證使用者輸入。使用者有三次機會來輸入正確的密碼,阻止使用者猜測密碼。
8、pass語句
Python中沒有對應的空大括弧或是分號( ; )來表示如C語言中的“不做任何事”,如果需要子語句塊的地方不寫任何語句, 解譯器會提示法錯誤。因此,Python 提供了pass語句,它不做任何事情,即NOP(No OPeration),pass同樣也可作為開發中的小技巧,標記以後將要完成的代碼。
def foo_func():
pass
這樣的代碼結構在開發和調試時很有用,因為編寫代碼的時候可能要先把結構定下來,但又不希望它幹擾其他已經完成的代碼, 在不需要它做任何事情地方放一個pass,將是一個很好的主意。另外它在異常處理中也被經常用到,比如你跟蹤到了一個非致命的錯誤而不想採取任何措施。
9、迭代器和iter()函數
(1)什麼是迭代器
迭代器為類序列對象提供了一個類序列的介面,可以利用它們的索引從0開始一直"迭代" 到序列的最後一個條目,用"計數"的方法迭代序列是很簡單的。 Python的迭代無縫地支援序列對象,而且它還允許程式員迭代非序列類型, 包括使用者定義物件。
迭代器用起來很靈巧,可以迭代不是序列但表現出序列行為的對象,例如字典的 key 、一個檔案的行等等。當使用迴圈迭代一個對象條目時,不必去關注它是迭代器還是序列。
(2)為什麼要迭代器
迭代器的定義:提供了可擴充的迭代器介面、對列表迭代帶來了效能上的增強、在字典迭代中效能提升、建立真正的迭代介面,而不是原來的隨機對象訪問、與所有已經存在的使用者定義的類以及擴充的類比序列和映射的對象向後相容、迭代非序列集合(例如映射和檔案)時, 可以建立更簡潔可讀的代碼。
(3)如何迭代
迭代器有一個next()方法的對象,而不是通過索引來計數。當一個迴圈機制(例如 for 語句)需要下一個項時,調用迭代器的next()方法就可以獲得它。條目全部取出後,會引發一個StopIteration異常,這並不表示錯誤發生,只是告訴外部調用者迭代完成。
不過,迭代器也有一些限制。 例如不能向後移動,不能回到開始,也不能複製一個迭代器。如果要再次(或者是同時)迭代同個對象,你只能去建立另一個迭代器對象。不過,還有其他的工具來協助你使用迭代器。
reversed()內建函數將返回一個反序訪問的迭代器。enumerate()內建函數同樣也返回迭代器。另外兩個新的內建函數:any()和 all(),如果迭代器中某個/所有條目的值都為布爾真時,則它們傳回值為真。
(4)使用迭代器
序列
在CODE上查看代碼片派生到My Code片
>>> myTuple = (123, 'xyz', 45.67) >>> i = iter(myTuple) >>> i.next() 123 >>> i.next() 'xyz' >>> i.next() 45.670000000000002 >>> i.next() Traceback (most recent call last): File "", line 1, in StopIteration
在for迴圈中for i in seq:do_something_to(i),它會自動調用迭代器的next()方法,並且監視StopIteration異常。
字典
字典和檔案是另外兩個可迭代的Python資料類型。字典的迭代器會遍曆它的鍵(keys),語句for eachKey in myDict.keys()可以縮寫為for eachKey in myDict。
Python還引進了三個新的內建字典方法來定義迭代: myDict.iterkeys() (通過 keys 迭代), myDict.itervalues() (通過 values 迭代)以及myDicit.iteritems() (通過key/value 對來迭代 )。 注意 : in操作符也可以用於檢查字典的key是否存在,布林運算式myDict.has_key(anyKey)可以被簡寫為anyKey in myDict。
檔案
檔案對象產生的迭代器會自動調用readline()方法。這樣,迴圈就可以訪問文字檔的所有行。可以使用更簡單的for eachLine in myFile替 換for eachLine in myFile.readlines()。
(5)可變對象和迭代器
在迭代可變對象的時候修改它們並不是個好主意,這在迭代器出現之前就是一個問題。一個序列的迭代器只是記錄當前到達第幾個元素,所以若在迭代時改變了元素,更新會立即反映到你所迭代的條目上。在迭代字典的key時,絕對不能改變這個字典。 使用字典的keys()方法是可以的,因為keys() 返回一個獨立於字典的列表, 而迭代器是與實際對象綁定在一起的,它將不會繼續執行下去。
(6)如何建立迭代器
對一個對象調用iter()就可以得到它的迭代器,它的文法如下:iter(obj)或iter(func, sentinel )。如果傳遞一個參數給iter(),它會檢查你傳遞的是不是一個序列,如果是則會根據索引從0一直迭代到序列結束。另一個建立迭代器的方法是使用類,一個實現__iter__()和next()方法的類可以作為迭代器使用。如果是傳遞兩個參數給iter(), 它會重複地調用func,直到迭代器的下個值等於sentinel。
10、列表解析
列表解析( List comprehensions或縮減為list comps ) 來自函數式程式設計語言Haskell。它是一個非常有用、簡單、而且靈活的工具, 可以用來動態地建立列表。
Python支援的函數式編程特性,例如lambda 、map() 以及filter()等,通過列表解析它們可以被簡化為一個列表解析式子。map()對所有的列表成員應用一個操作,filter()基於一個條件運算式過濾列表成員,lambda()允許快速地建立只有一行的函數對象。
列表解析的文法:[expr for iter_var in iterable], 它迭代iterable對象的所有條目。其中的expr應用於序列的每個成員,最後的結果值是該運算式產生的列表,迭代變數並不需要是運算式的一部分。
在CODE上查看代碼片派生到My Code片
>>> [x ** 2 for x in range(6)] [0, 1, 4, 9, 16, 25]
列表解析的運算式可以取代內建的map()函數以及lambda ,而且效率更高。結合 if語句,列表解析還提供了一個擴充版本的文法:[expr for iter_var in iterable if cond_expr],它在迭代時會過濾/捕獲滿足條件運算式cond_expr的序列成員。
挑選出序列中的奇數:
在CODE上查看代碼片派生到My Code片
>>> seq = [11, 10, 9, 9, 10, 10, 9, 8, 23, 9, 7, 18, 12, 11, 12] >>> filter(lambda x: x % 2, seq) [11, 9, 9, 9, 23, 9, 7, 11] >>> [x for x in seq if x % 2] [11, 9, 9, 9, 23, 9, 7, 11]
即使不用filter()和lambda,可以使用列表解析來完成操作,獲得想要的數字。
矩陣範例:迭代一個有三行五列的矩陣,[(x+1,y+1) for x in range(3) for y in range(5)]。
磁碟檔案範例:若有一個資料檔案text.txt,需要計算出所有非空白字元的數目,可以把每行分割( split )為單詞,,然後計算單詞個數:>>> f = open('hhga.txt', 'r');len([word for line in f for word in line.split()])。快速地計算檔案大小:>>>import os;os.stat('text.txt').st_size。把每個單詞的長度加起來:>>>f.seek(0);sum([len(word) for line in f for word in line.split()])。
11、產生器運算式
產生器運算式是列表解析的一個擴充,只用一行代碼就可以建立包含特定內容的列表。另一個重要特性是產生器,產生器是特定的函數,允許返回一個值,然後"暫停"代碼的執行,稍後恢複。
列表解析的一個不足就是必要產生所有的資料,用以建立整個列表。這可能對有大量資料的迭代器有負面效應, 產生器運算式通過結合列表解析和產生器解決了這個問題。
產生器運算式與列表解析非常相似,而且它們的基本文法基本相同。不過它並不真正建立數字列表而是返回一個產生器,這個產生器在每次計算出一個條目後,把這個條目“產生”(yield)出來。產生器運算式使用了"延遲計算"(lazy evaluation),所以它在使用記憶體上更有效。產生器並不會讓列表解析廢棄,它只是一個記憶體使用量更友好的結構,基於此,有很多使用產生器地方。
列表解析文法:
[expr for iter_var in iterable if cond_expr]
產生器運算式文法:
(expr for iter_var in iterable if cond_expr)
磁碟檔案範例:上述計算文字檔中非空白字元總和,如果這個檔案的大小變得很大, 那麼這行代碼的記憶體效能會很低,因為要建立一個很長的列表用於存放單詞的長度。為了避免建立龐大的列表,使用產生器運算式來完成求和操作,最佳化後的代碼:>>> sum(len(word) for line in data for word in line.split()),是把方括弧刪除,少了兩位元組, 而且更節省記憶體。
交叉配對例子:成器運算式就好像是懶惰的列表解析(這反而成了它主要的優勢),它還可以用來處理其他列表或產生器,如:x_product_pairs = ((i, j) for i in rows for j in cols())。
重構範例,尋找檔案最長的行的例子:
以前的方法:
在CODE上查看代碼片派生到My Code片
#!/usr/bin/env python def fun(): f = open('/etc/motd', 'r') longest = 0 allLines = [x.strip() for x in f.readlines()] #or allLineLens = [len(x.strip()) for x in f] f.close() for line in allLines: linelen = len(line) if linelen > longest: #or longest = max(allLineLens) longest = linelen return longest
新的方法:
使用產生器運算式其他清單解析和max()函數,並去掉檔案開啟模式(預設為讀取):return max(len(x.strip()) for x in open('/etc/motd'))。
三、總結
(1)itertools模組被加入,更加支援了迭代器的應用,列表解析和產生運算式的內容可結合執行個體分析。
(2)若有不足,請留言,在此先感謝!