續上篇
break 陳述式,終止包含它的最內部 for 或 while 迴圈。
for 和 while 迴圈可帶有一個 else 子句
在 for 迴圈中,else 子句會在迴圈到達最終的疊代後執行。
在 while 迴圈中,它會在迴圈條件變為 false 後執行。
在任何一種迴圈中,如果迴圈由 break 終止,則不會執行 else 子句。
下面的 for 迴圈對此進行了舉例說明,該迴圈用以搜索質數:
(沒錯,這是正確的程式碼。請看仔細:else 子句屬於 for 迴圈,並非 if 陳述式。)
當 else 子句用於迴圈時,相較於搭配 if 陳述式使用,它的行為與 try 陳述式中的 else 子句更為相似:try 陳述式的 else 子句在沒有發生例外 (exception) 時執行,而迴圈的 else 子句在沒有任何 break 發生時執行。更多有關 try 陳述式和例外的介紹,見處理例外。
continue 陳述式,亦承襲於 C 語言,讓所屬的迴圈繼續執行下個疊代:
pass 陳述式不執行任何動作。它可用在語法上需要一個陳述式但程式不需要執行任何動作的時候。例如:
果然跑不出東西
這經常用於建立簡單的 class(類別):
pass 亦可作為一個函式或條件判斷主體的預留位置,在你撰寫新的程式碼時讓你保持在更抽象的思維層次。pass 會直接被忽略:
match 陳述式會拿取一個運算式,並將其值與多個連續的模式 (pattern) 進行比較,這些模式是以一個或多個 case 區塊來表示。表面上,這類似 C、Java 或 JavaScript(以及許多其他語言)中的 switch 陳述式,但它與 Rust 或 Haskell 等語言中的模式匹配 (pattern matching) 更為相近。只有第一個匹配成功的模式會被執行,而它也可以將成分(序列元素或物件屬性)從值中提取到變數中。
最簡單的形式,是將一個主題值 (subject value) 與一個或多個字面值 (literal) 進行比較:請注意最後一段:「變數名稱」_ 是作為通用字元 (wildcard)的角色,且永遠不會匹配失敗。如果沒有 case 匹配成功,則不會執行任何的分支。
你可以使用 |(「或」)來將多個字面值組合在單一模式中:
模式可以看起來像是拆解賦值 (unpacking assignment),且可以用來連結變數:
請仔細研究那個例子!第一個模式有兩個字面值,可以想作是之前所述的字面值模式的延伸。但是接下來的兩個模式結合了一個字面值和一個變數,且該變數繫結 (bind) 了來自主題 (point) 的一個值。第四個模式會擷取兩個值,這使得它在概念上類似於拆解賦值 (x, y) = point。
如果你要用 class 來結構化你的資料,你可以使用該 class 的名稱加上一個引數列表,類似一個建構式 (constructor),但它能夠將屬性擷取到變數中:
你可以將位置參數 (positional parameter) 與一些能夠排序其屬性的內建 class(例如 dataclasses)一起使用。你也可以透過在 class 中設定特殊屬性 match_args,來定義模式中屬性們的特定位置。如果它被設定為 ("x", "y"),則以下的模式都是等價的(且都會將屬性 y 連結到變數 var):
理解模式的一種推薦方法,是將它們看作是你會放在賦值 (assignment) 左側內容的一種延伸形式,這樣就可以了解哪些變數會被設為何值。只有獨立的名稱(像是上面的 var)能被 match 陳述式賦值。點分隔名稱(如 foo.bar)、屬性名稱(上面的 x= 及 y=)或 class 名稱(由它們後面的 "(...)" 被辨識,如上面的 Point)則永遠無法被賦值。
模式可以任意地被巢套 (nested)。例如,如果我們有一個由某些點所組成的簡短 list,我們就可以像這樣加入 match_args 來對它進行匹配:
我們可以在模式中加入一個 if 子句,稱為「防護 (guard)」。如果該防護為假,則 match 會繼續嘗試下一個 case 區塊。請注意,值的擷取會發生在防護的評估之前:
此種陳述式的其他幾個重要特色:
關於更詳細的解釋和其他範例,你可以閱讀 PEP 636,它是以教學的格式編寫而成。
我們可以建立一個函式來產生費式數列到任何一個上界:
關鍵字 def 介紹一個函式的定義。它之後必須連著該函式的名稱和置於括號之中的一串參數。自下一行起,所有縮排的陳述式成為該函式的主體。
一個函式的第一個陳述式可以是一個字串文本;該字串文本被視為該函式的說明文件字串,即 docstring。(關於 docstring 的細節請參見說明文件字串 (Documentation Strings)段落。)有些工具可以使用 docstring 來自動產生線上或可列印的文件,或讓使用者能以互動的方式在原始碼中瀏覽文件。在原始碼中加入 docstring 是個好慣例,應該養成這樣的習慣。
函式執行時會建立一個新的符號表 (symbol table) 來儲存該函式內的區域變數 (local variable)。更精確地說,所有在函式內的變數賦值都會把該值儲存在一個區域符號表。然而,在引用一個變數時,會先從區域符號表開始搜尋,其次為外層函式的區域符號表,其次為全域符號表 (global symbol table),最後為所有內建的名稱。因此,在函式中,全域變數及外層函式變數雖然可以被引用,但無法被直接賦值(除非全域變數是在 global 陳述式中被定義,或外層函式變數在 nonlocal 陳述式中被定義)。
在一個函式被呼叫的時候,實際傳入的參數(引數)會被加入至該函式的區域符號表。因此,引數傳入的方式為傳值呼叫 (call by value)(這裡傳遞的值永遠是一個物件的參照 (reference),而不是該物件的值)。 [1] 當一個函式呼叫別的函式或遞迴呼叫它自己時,在被呼叫的函式中會建立一個新的區域符號表。
函式定義時,會把該函式名稱加入至當前的符號表。函式名稱的值帶有一個型別,並被直譯器辨識為使用者自定函式 (user-defined function)。該值可以被指定給別的變數名,使該變數名也可以被當作函式使用。這是常見的重新命名方式:
如果你是來自別的語言,你可能不同意 fib 是個函式,而是個程序 (procedure),因為它並沒有回傳值。實際上,即使一個函式缺少一個 return 陳述式,它亦有一個固定的回傳值。這個值稱為 None(它是一個內建名稱)。在直譯器中單獨使用 None 時,通常不會被顯示。你可以使用 print() 來看到它:
如果要寫一個函式回傳費式數列的 list 而不是直接印出它,這也很容易:
這個例子一樣示範了一些新的 Python 特性:
result = result + [a]
,但更有效率。