《Python3.6官方文件》– 第四章 更多流程控制語句
4 更多流程控制語句
除了剛才介紹的while語句,Python也引入了其它語言常見的流程控制語法,並稍作變化。
4.1 if語句
或許最廣為人知的控制語句就是if語句。例如:
>>> x = int(input("Please enter an integer: "))
Please enter an integer: 42
>>> if x < 0:
... x = 0
... print('Negative changed to zero')
... elif x == 0:
... print('Zero')
... elif x == 1:
... print('Single')
... else:
... print('More')
...
More
if語句可以有零個或者更多elif
分支,else
分支是非強制要求有的。關鍵詞‘elif’是比‘else if’短的,這可以有效避免過度縮排問題。而且,if … elif … elif …
語句可以替換其它語言中的switch/case
語句。
4.2. for Statements
Python中的for語句與你可能用過的C或者Pascal語言中的for語句有點不同。與通常通過一個等差數列進行迭代(像Pascal語言),或者給予使用者自主迭代和終止步驟(就像C語言),Python的For語句以集合(list或者string)中的元素為迭代單元,按照它們在集合中的順序逐個迭代。例如(我並沒有含沙射影):
>>> # Measure some strings:
... words = ['cat', 'window', 'defenestrate']
>>> for w in words:
... print(w, len(w))
...
cat 3
window 6
defenestrate 12
如果你需要在迭代中時修改集合(例如為集合複製某個元素),我們建議你先為集合建立副本。直接迭代集合並不能為集合建立副本,切片操作可以很方便的做到這一點。
>>> for w in words[:]: # Loop over a slice copy of the entire list.
... if len(w) > 6:
... words.insert(0, w)
...
>>> words
['defenestrate', 'cat', 'window', 'defenestrate']
靠著使用for w in words:
語法,我們可以用不斷插入defenestrate
單詞,來嘗試創建出很大的集合。
4.3. range()函式
如果你需要迭代一組連續數字,內建函式range()正好可以幫上忙。它生成遞增數列:
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
指定的尾數入參永遠不是生成序列的一部分; range(10)生成十個數字,代表著會生成十個長度的序列。我們甚至可以讓序列從另一個數字開始,或者指定另一個遞增值(儘管被否認,但它有時被稱為‘步長’):
range(5, 10)
5, 6, 7, 8, 9
range(0, 10, 3)
0, 3, 6, 9
range(-10, -100, -30)
-10, -40, -70
為了迭代集合的索引,我們可以把range()函式和len()函式結合起來使用。如下:
>>> a = ['Mary', 'had', 'a', 'little', 'lamb']
>>> for i in range(len(a)):
... print(i, a[i])
...
0 Mary
1 had
2 a
3 little
4 lamb
然而在大多數此類情況下,使用內建函式enumerate()更方便,參見Looping Techniques章節
如果你列印一個range函式,會發生奇怪的事情:
>>> print(range(10))
range(0, 10)
從很多特性上看,rang()函式返回的物件都表現的像是一個list,但實際上它不是。當你迭代它時,它返回了你想要的連續數字,但是它為了節省記憶體空間,不是個真正的list。
我們稱這種物件為可迭代的,即適合成為可以期待得到有限連續項的函式或者結構的目標。我們已經知道,for語句是一個類似的迭代器,list()函式也是一個類似的迭代器;它以可迭代物件建立list。
>>> list(range(5))
[0, 1, 2, 3, 4]
稍後我們可以看到更多函式可以返回可迭代物件,或者把可迭代物件作為入參。
4.4.迴圈中的break、continue語句和else子句
break語句,就像在C語言中一樣,跳出最內層的for或者while迴圈。
迴圈語句也許有一個else子句;它在for語句的list迴圈終結時或者while語句的條件變為false時執行,但在迴圈被break終結時它並不執行。下面以搜尋素數為典型示例演示下:
>>> for n in range(2, 10):
... for x in range(2, n):
... if n % x == 0:
... print(n, 'equals', x, '*', n//x)
... break
... else:
... # loop fell through without finding a factor
... print(n, 'is a prime number')
...
2 is a prime number
3 is a prime number
4 equals 2 * 2
5 is a prime number
6 equals 2 * 3
7 is a prime number
8 equals 2 * 4
9 equals 3 * 3
(是的,這是一段沒有錯誤的程式碼。仔細看:else子句是屬於for迴圈,而不是if語句)
當與迴圈一起使用時,它的else子句與在try語句塊和if語句塊的else子句功能類似:當try語句塊沒有異常時,else分支觸發執行;當迴圈沒有被break終止時,else分支觸發執行。想要學習更多try語句塊和異常,參見Handling Exceptions
continue語句,是從C語言借鑑的,直接執行迴圈的下一次。
>>> for num in range(2, 10):
... if num % 2 == 0:
... print("Found an even number", num)
... continue
... print("Found a number", num)
Found an even number 2
Found a number 3
Found an even number 4
Found a number 5
Found an even number 6
Found a number 7
Found an even number 8
Found a number 9
4.5. pass語句
pass語句什麼也不執行。它可以背用於語法需要,但程式並不想執行任何任務。例如:
>>> while True:
... pass # Busy-wait for keyboard interrupt (Ctrl+C)
...
它通常被用於建立一個最小的類:
>>> class MyEmptyClass:
... pass
...
另一個用處是,當你正在寫新的程式碼,可以將pass寫在函式體或者條件子句體中,用以標記佔位,這使你在更抽象層面思考。pass會被自動忽略。
>>> def initlog(*args):
... pass # Remember to implement this!
...
4.6 函式定義
我們可以寫一個可以輸出不超過指定邊界值的斐波那契數列的函式:
>>> def fib(n): # write Fibonacci series up to n
... """Print a Fibonacci series up to n."""
... a, b = 0, 1
... while a < n:
... print(a, end=' ')
... a, b = b, a+b
... print()
...
>>> '# Now call the function we just defined:
... fib(2000)
0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597
關鍵詞def定義了一個函式。它後邊是函式名字和被括號包括起來的一串引數。函式體的宣告是從下一行開始的,而且必須縮排。
函式體的第一行可以是一個隨意的字串陣列;字串陣列是函式的文件字串或者docstring。(更多關於docstring的說明可以在文件API部分找到)。有一些工具可以使用docstrings製作線上文件或者列印文件,或者讓使用者互動式的瀏覽程式碼;在程式碼中寫入docstrings是一個很好的做法,你應該養成習慣。
函式執行時會為區域性變數引入一個新的符號表。所有的區域性變數都儲存在這個區域性符號表中。引用引數時,會先從區域性符號表中查詢,然後是全域性符號表,然後是內建命名錶。因此,全域性引數雖然可以被引用,但它們不能在函式中直接賦值(除非它們在全域性宣告語句中)。
函式引用的實際引數在函式呼叫時引入區域性符號表,因此,實參總是傳值呼叫(這裡的值指的是物件引用,而不是該物件的值)。[1] 一個函式被另一個函式呼叫時,一個新的區域性符號表在呼叫過程中被建立。
函式的定義過程會在函式當前引數列表前定義函式名字。作為使用者定義函式,函式名有一個為直譯器認可的型別值。這個值可以賦給其它命名,使其能夠作為一個函式來使用。這就像一個重新命名機制:
>>>
>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
類比其它程式語言,你可能認為fib不是一個函式( function ),而是一個過程( procedure )。Python 和 C 一樣,過程只是一個沒有返回值的函式。實際上,沒有顯式宣告返回值的過程也有一個返回值,雖然是一個不討人喜歡的值。這個值被稱為 None (這是一個內建命名)。如果一個值只是 None 的話,通常直譯器不會寫一個 None 出來,如果你真想要檢視它的話,可以這樣做:
>>>
>>> fib(0)
>>> print(fib(0))
None
我們來寫一個簡單的示例,用於演示如何從函式中返回一個包含菲波那契數列的數值連結串列,而不是列印它: 、
>>>
>>> def fib2(n): # return Fibonacci series up to n
... """Return a list containing the Fibonacci series up to n."""
... result = []
... a, b = 0, 1
... while a < n:
... result.append(a) # see below
... a, b = b, a+b
... return result
...
>>> f100 = fib2(100) # call it
>>> f100 # write the result
[0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89]
和以往一樣,上面的例子演示了一些新的 Python 功能:
return 語句從函式中返回一個值,不帶表示式的 return 返回 None。過程結束後也會返回 None 。
語句result.append(b)稱為連結串列物件 result 的一個函式。函式是一個“屬於”某個物件的,它被命名為 obj.methodename,這裡的 obj 是某個物件(可能是一個表示式),methodename是某個在該物件型別定義中的方法的命名。不同的入參型別定義不同的方法。不同入參型別也有可能名字相同,但不會混淆。(當你定義自己的物件型別和方法時,可能會出現這種情況,本指南後面的章節會介紹如何使用型別)。示例中演示的 append()方法由連結串列物件定義,它向連結串列中加入一個新元素。在示例中它等同於””result = result + [b]””,不過效率更高。
(全文完)
閱讀原文