iT邦幫忙

2024 iThome 鐵人賽

DAY 8
0
Python

30天導讀 Python Software Foundation 官方翻譯文件系列 第 8

Day 7 -深入了解流程控制~導讀 Python Software Foundation 教學文件

  • 分享至 

  • xImage
  •  

續上篇

深入了解流程控制

4.4. 迴圈內的 break 和 continue 陳述式及 else 子句

break 陳述式,終止包含它的最內部 for 或 while 迴圈。
for 和 while 迴圈可帶有一個 else 子句
在 for 迴圈中,else 子句會在迴圈到達最終的疊代後執行。
在 while 迴圈中,它會在迴圈條件變為 false 後執行。
在任何一種迴圈中,如果迴圈由 break 終止,則不會執行 else 子句。
下面的 for 迴圈對此進行了舉例說明,該迴圈用以搜索質數:
https://ithelp.ithome.com.tw/upload/images/20240922/201626738IyV0xoO6L.png
(沒錯,這是正確的程式碼。請看仔細:else 子句屬於 for 迴圈,並非 if 陳述式。)
當 else 子句用於迴圈時,相較於搭配 if 陳述式使用,它的行為與 try 陳述式中的 else 子句更為相似:try 陳述式的 else 子句在沒有發生例外 (exception) 時執行,而迴圈的 else 子句在沒有任何 break 發生時執行。更多有關 try 陳述式和例外的介紹,見處理例外
continue 陳述式,亦承襲於 C 語言,讓所屬的迴圈繼續執行下個疊代:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673hP58OauDth.png


4.5. pass 陳述式

pass 陳述式不執行任何動作。它可用在語法上需要一個陳述式但程式不需要執行任何動作的時候。例如:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673ousjf5Zqgq.png
果然跑不出東西

這經常用於建立簡單的 class(類別):
https://ithelp.ithome.com.tw/upload/images/20240922/20162673nl6TiDGSeN.png

pass 亦可作為一個函式或條件判斷主體的預留位置,在你撰寫新的程式碼時讓你保持在更抽象的思維層次。pass 會直接被忽略:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673VoJ8gNrfKP.png


4.6. match 陳述式

match 陳述式會拿取一個運算式,並將其值與多個連續的模式 (pattern) 進行比較,這些模式是以一個或多個 case 區塊來表示。表面上,這類似 C、Java 或 JavaScript(以及許多其他語言)中的 switch 陳述式,但它與 Rust 或 Haskell 等語言中的模式匹配 (pattern matching) 更為相近。只有第一個匹配成功的模式會被執行,而它也可以將成分(序列元素或物件屬性)從值中提取到變數中。
最簡單的形式,是將一個主題值 (subject value) 與一個或多個字面值 (literal) 進行比較:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673S0r7zazmEt.png
請注意最後一段:「變數名稱」_ 是作為通用字元 (wildcard)的角色,且永遠不會匹配失敗。如果沒有 case 匹配成功,則不會執行任何的分支。

你可以使用 |(「或」)來將多個字面值組合在單一模式中:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673RPc4MMmekt.png

模式可以看起來像是拆解賦值 (unpacking assignment),且可以用來連結變數:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673o2ZTsGSAT4.png

請仔細研究那個例子!第一個模式有兩個字面值,可以想作是之前所述的字面值模式的延伸。但是接下來的兩個模式結合了一個字面值和一個變數,且該變數繫結 (bind) 了來自主題 (point) 的一個值。第四個模式會擷取兩個值,這使得它在概念上類似於拆解賦值 (x, y) = point。
如果你要用 class 來結構化你的資料,你可以使用該 class 的名稱加上一個引數列表,類似一個建構式 (constructor),但它能夠將屬性擷取到變數中:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673CLHoxxUoMD.png

你可以將位置參數 (positional parameter) 與一些能夠排序其屬性的內建 class(例如 dataclasses)一起使用。你也可以透過在 class 中設定特殊屬性 match_args,來定義模式中屬性們的特定位置。如果它被設定為 ("x", "y"),則以下的模式都是等價的(且都會將屬性 y 連結到變數 var):
https://ithelp.ithome.com.tw/upload/images/20240922/201626739RCBLKzp0V.png

理解模式的一種推薦方法,是將它們看作是你會放在賦值 (assignment) 左側內容的一種延伸形式,這樣就可以了解哪些變數會被設為何值。只有獨立的名稱(像是上面的 var)能被 match 陳述式賦值。點分隔名稱(如 foo.bar)、屬性名稱(上面的 x= 及 y=)或 class 名稱(由它們後面的 "(...)" 被辨識,如上面的 Point)則永遠無法被賦值。
模式可以任意地被巢套 (nested)。例如,如果我們有一個由某些點所組成的簡短 list,我們就可以像這樣加入 match_args 來對它進行匹配:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673872niIXjzx.png

我們可以在模式中加入一個 if 子句,稱為「防護 (guard)」。如果該防護為假,則 match 會繼續嘗試下一個 case 區塊。請注意,值的擷取會發生在防護的評估之前:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673RAvI64Zdc8.png

此種陳述式的其他幾個重要特色:

  • 與拆解賦值的情況類似,tuple(元組)和 list 模式具有完全相同的意義,而且實際上可以匹配任意的序列。一個重要的例外,是它們不能匹配疊代器 (iterator) 或字串。
  • 序列模式 (sequence pattern) 可支援擴充拆解 (extended unpacking):[x, y, *rest] 與 (x, y, rest) 的作用類似於拆解賦值。 後面的名稱也可以是 ,所以 (x, y, *) 會匹配一個至少兩項的序列,且不會連結那兩項以外的其餘項。
  • 映射模式 (mapping pattern):{"bandwidth": b, "latency": l} 能從一個 dictionary(字典)中擷取 "bandwidth" 及 "latency" 的值。與序列模式不同,額外的鍵 (key) 會被忽略。一種像是 **rest 的拆解方式,也是可被支援的。(但 **_ 則是多餘的做法,所以它並不被允許。)
  • 使用關鍵字 as 可以擷取子模式 (subpattern):
    https://ithelp.ithome.com.tw/upload/images/20240922/20162673rbhzO7oDif.png
    將會擷取輸入的第二個元素作為 p2(只要該輸入是一個由兩個點所組成的序列)。
  • 大部分的字面值是藉由相等性 (equality) 來比較,但是單例物件 (singleton) True、False 和 None 是藉由標識值 (identity) 來比較。
  • 模式可以使用附名常數 (named constant)。這些模式必須是點分隔名稱,以免它們被解釋為擷取變數:
    https://ithelp.ithome.com.tw/upload/images/20240922/20162673RmVEBBPxUM.png

關於更詳細的解釋和其他範例,你可以閱讀 PEP 636,它是以教學的格式編寫而成。


4.7. 定義函式 (function)

我們可以建立一個函式來產生費式數列到任何一個上界:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673okXfoD67Dn.png

關鍵字 def 介紹一個函式的定義。它之後必須連著該函式的名稱和置於括號之中的一串參數。自下一行起,所有縮排的陳述式成為該函式的主體。
一個函式的第一個陳述式可以是一個字串文本;該字串文本被視為該函式的說明文件字串,即 docstring。(關於 docstring 的細節請參見說明文件字串 (Documentation Strings)段落。)有些工具可以使用 docstring 來自動產生線上或可列印的文件,或讓使用者能以互動的方式在原始碼中瀏覽文件。在原始碼中加入 docstring 是個好慣例,應該養成這樣的習慣。
函式執行時會建立一個新的符號表 (symbol table) 來儲存該函式內的區域變數 (local variable)。更精確地說,所有在函式內的變數賦值都會把該值儲存在一個區域符號表。然而,在引用一個變數時,會先從區域符號表開始搜尋,其次為外層函式的區域符號表,其次為全域符號表 (global symbol table),最後為所有內建的名稱。因此,在函式中,全域變數及外層函式變數雖然可以被引用,但無法被直接賦值(除非全域變數是在 global 陳述式中被定義,或外層函式變數在 nonlocal 陳述式中被定義)。
在一個函式被呼叫的時候,實際傳入的參數(引數)會被加入至該函式的區域符號表。因此,引數傳入的方式為傳值呼叫 (call by value)(這裡傳遞的值永遠是一個物件的參照 (reference),而不是該物件的值)。 [1] 當一個函式呼叫別的函式或遞迴呼叫它自己時,在被呼叫的函式中會建立一個新的區域符號表。

函式定義時,會把該函式名稱加入至當前的符號表。函式名稱的值帶有一個型別,並被直譯器辨識為使用者自定函式 (user-defined function)。該值可以被指定給別的變數名,使該變數名也可以被當作函式使用。這是常見的重新命名方式:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673LfOob057tU.png

如果你是來自別的語言,你可能不同意 fib 是個函式,而是個程序 (procedure),因為它並沒有回傳值。實際上,即使一個函式缺少一個 return 陳述式,它亦有一個固定的回傳值。這個值稱為 None(它是一個內建名稱)。在直譯器中單獨使用 None 時,通常不會被顯示。你可以使用 print() 來看到它:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673TZFHw4nhX3.png

如果要寫一個函式回傳費式數列的 list 而不是直接印出它,這也很容易:
https://ithelp.ithome.com.tw/upload/images/20240922/20162673kjJqwvc5CW.png

這個例子一樣示範了一些新的 Python 特性:

  • return 陳述式會讓一個函式回傳一個值。單獨使用 return 不外加一個運算式作為引數時會回傳 None。一個函式執行到結束也會回傳 None。
  • result.append(a) 陳述式呼叫了一個 list 物件 result 的 method(方法)。method 為「屬於」一個物件的函式,命名規則為 obj.methodname,其中 obj 為某個物件(亦可為一運算式),而 methodname 為該 method 的名稱,並由該物件的型別所定義。不同的型別定義不同的 method。不同型別的 method 可以擁有一樣的名稱而不會讓 Python 混淆。(你可以使用 class(類別)定義自己的物件型別和 method,見 Class(類別))範例中的 append() method 定義在 list 物件中;它會在該 list 的末端加入一個新的元素。這個例子等同於 result = result + [a],但更有效率。

上一篇
Day 6 -深入了解流程控制~導讀 Python Software Foundation 教學文件
下一篇
Day 8 -深入了解流程控制~導讀 Python Software Foundation 教學文件
系列文
30天導讀 Python Software Foundation 官方翻譯文件14
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言