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
可以有零個或多個 elif 段落,且 else 段落可有可無。關鍵字 elif 只是「else if」的縮寫,並且用來避免過多的縮排。一個 if … elif … elif … 序列可以用來替代其他語言中出現的 switch 或 case 陳述式。
4.2. for 陳述式¶
在 Python 中的 for 陳述式可能不同於在 C 或 Pascal 中所看到的使用方式。與其只能选代 (iterate) 一個等差級數(如 Pascal),或給與使用者定義选代步進方式與結束條件(如 C),Python 的 for 陳述选代任何序列(list 或者字串)的元素,以他們出現在序列中的順序。例如(無意雙關):
>>> # Measure some strings:
... words = ['cat', 'window', 'defenestrate']
>>> for w in words:
... print(w, len(w))
...
cat 3
window 6
defenestrate 12
如果你在迴圈中需要修改一個你正在选代的序列(例如重複一些選擇的元素),那麼會建議你先建立一個序列的拷貝。选代序列並不暗示建立新的拷貝。此時 slice 語法就讓這件事十分容易完成:
>>> 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']
With for w in words:, the example would attempt to create an infinite list,
inserting defenestrate over and over again.
4.3. range() 函式¶
如果你需要选代一個數列的話,使用內建 range() 函式就很方便。它可以生成一等差級數:
>>> for i in range(5):
... print(i)
...
0
1
2
3
4
給定的結束值永遠不會出現在生成的序列中;range(10) 生成的 10 個數值,即對應存取一個長度為 10 的序列內每一個元素的索引值。也可以讓 range 從其他數值計數,或者給定不同的級距(甚至為負;有時稱之為 『step』):
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() 函式將更為方便,詳見迴圈技巧。
如果直接印出一個 range 則會出現奇怪的輸出:
>>> print(range(10))
range(0, 10)
在很多情況下,由 range() 回傳的物件的行為如同一個 list,但實際上它並不是。它是一個物件在你选代時會回傳想要的序列的連續元素,並不會真正建出這個序列的 list,以節省空間。
我們稱這樣的物件為 iterable(可选代的),意即能作為函式、陳述式中能一直獲取連續元素直到用盡的部件。我們已經看過 for 陳述式可做為如此的 iterator(选代器)。list() 函式為另一個例子,他可以自 iterable(可选代物件)建立 list:
>>> list(range(5))
[0, 1, 2, 3, 4]
待會我們可以看到更多函式回傳 iterable 和接受 iterable 為引數。
4.4. break 和 continue 陳述、迴圈內 else 段落¶
The break statement, like in C, breaks out of the innermost enclosing
for or while loop.
迴圈可以帶有一個 else 段落。當迴圈歷遍选代的 list (在 for 中)或條件為偽(在 while 中)時,這個段落會被執行;但迴圈被 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 段落與迴圈使用時,相較於搭配 if 陳述使用,它的行為與搭配 try 陳述使用時更為相似:try 的 else 段落在沒有任何例外 (exception) 時執行,而迴圈的 else 段落在沒有任何 break 時執行。更多有關 try 陳述和例外的介紹,見處理例外。
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. 定義函式 (function)¶
We can create a function that writes the Fibonacci series to an arbitrary boundary:
>>> 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
The keyword def introduces a function definition. It must be
followed by the function name and the parenthesized list of formal parameters.
The statements that form the body of the function start at the next line, and
must be indented.
The first statement of the function body can optionally be a string literal; this string literal is the function’s documentation string, or docstring. (More about docstrings can be found in the section 說明文件字串.) There are tools which use docstrings to automatically produce online or printed documentation, or to let the user interactively browse through code; it’s good practice to include docstrings in code that you write, so make a habit of it.
The execution of a function introduces a new symbol table used for the local
variables of the function. More precisely, all variable assignments in a
function store the value in the local symbol table; whereas variable references
first look in the local symbol table, then in the local symbol tables of
enclosing functions, then in the global symbol table, and finally in the table
of built-in names. Thus, global variables cannot be directly assigned a value
within a function (unless named in a global statement), although they
may be referenced.
The actual parameters (arguments) to a function call are introduced in the local symbol table of the called function when it is called; thus, arguments are passed using call by value (where the value is always an object reference, not the value of the object). [1] When a function calls another function, a new local symbol table is created for that call.
A function definition introduces the function name in the current symbol table. The value of the function name has a type that is recognized by the interpreter as a user-defined function. This value can be assigned to another name which can then also be used as a function. This serves as a general renaming mechanism:
>>> fib
<function fib at 10042ed0>
>>> f = fib
>>> f(100)
0 1 1 2 3 5 8 13 21 34 55 89
Coming from other languages, you might object that fib is not a function but
a procedure since it doesn’t return a value. In fact, even functions without a
return statement do return a value, albeit a rather boring one. This
value is called None (it’s a built-in name). Writing the value None is
normally suppressed by the interpreter if it would be the only value written.
You can see it if you really want to using
