像計算機科學家一樣思考:用 Python 學習 第 2 版/函式
在程式設計的上下文中,函式是一系列命名的語句,用於執行所需的運算。此運算在函式定義中指定。在 Python 中,函式定義的語法如下:
您可以為建立的函式取任何您想要的名稱,但不能使用 Python 關鍵字作為名稱。引數列表指定為了使用新函式,您必須提供哪些資訊(如果有)。
函式內部可以有任意數量的語句,但它們必須從def縮排。在本手冊中的示例中,我們將使用四個空格的標準縮排。函式定義是我們將看到的幾個複合語句中的第一個,它們都具有相同的模式
- 一個頭部,以關鍵字開頭,以冒號結尾。
- 一個主體,由一個或多個 Python 語句組成,每個語句都從頭部縮排相同的量——4 個空格是 Python 標準。
在函式定義中,頭部中的關鍵字是def,後面跟著函式名稱和一個用括號括起來的引數列表。引數列表可以為空,也可以包含任意數量的引數。無論哪種情況,括號都是必需的。
我們將要編寫的頭幾個函式沒有引數,因此語法如下所示
此函式名為new_line。空括號表示它沒有引數。它的主體僅包含一個語句,該語句輸出一個換行符。(當您使用沒有引數的print命令時,就會發生這種情況。)
定義新函式不會使函式執行。為此,我們需要一個函式呼叫。函式呼叫包含要執行的函式的名稱,後跟一個值列表,稱為實參,這些值被分配給函式定義中的引數。我們的第一個示例具有空引數列表,因此函式呼叫不接受任何實參。但是,請注意,函式呼叫中需要括號
此程式的輸出為
兩行之間的額外空格是new_line()函式呼叫導致的。如果我們想要在行之間新增更多空格怎麼辦?我們可以重複呼叫同一個函式
或者我們可以編寫一個名為three_lines的新函式,它列印三個換行符
此函式包含三個語句,所有語句都縮進了四個空格。由於下一個語句沒有縮排,因此 Python 知道它不是函式的一部分。
您應該注意此程式的一些內容
- 您可以重複呼叫相同的過程。事實上,這樣做非常普遍且有用。
- 您可以讓一個函式呼叫另一個函式;在本例中three_lines呼叫new_line.
到目前為止,建立所有這些新函式的價值可能還不清楚。實際上,有很多原因,但此示例演示了兩個原因
- 建立新函式使您有機會命名一組語句。函式可以透過將複雜的計算隱藏在單個命令後面,以及使用英文單詞代替神秘的程式碼來簡化程式。
- 建立新函式可以透過消除重複程式碼來使程式更小。例如,列印九個連續換行符的簡短方法是呼叫three_lines三次。
將上一節中的程式碼片段合併到名為tryme1.py的指令碼中,整個程式如下所示
此程式包含兩個函式定義new_line和three_lines。函式定義像其他語句一樣被執行,但效果是建立新函式。函式內部的語句在函式被呼叫之前不會被執行,並且函式定義不會生成任何輸出。
正如您可能預期的那樣,您必須在執行函式之前建立它。換句話說,函式定義必須在第一次呼叫它之前執行。
為了確保函式在第一次使用之前已定義,您必須知道語句執行的順序,這稱為執行流程。
執行始終從程式的第一條語句開始。語句按順序從上到下依次執行。
函式定義不會改變程式的執行流程,但請記住,函式內部的語句在函式被呼叫之前不會被執行。雖然不常見,但您可以在另一個函式內部定義一個函式。在這種情況下,內部定義在外部函式被呼叫之前不會被執行。
函式呼叫就像執行流程中的一個繞路。程式不會繼續執行下一條語句,而是跳轉到被呼叫函式的第一行,執行那裡的所有語句,然後返回到它離開的地方繼續執行。
這聽起來很簡單,直到您記得一個函式可以呼叫另一個函式。在執行一個函式的過程中,程式可能需要執行另一個函式中的語句。但在執行該新函式時,程式可能需要執行另一個函式!
幸運的是,Python 擅長跟蹤其所在位置,因此每次函式完成時,程式都會在呼叫它的函式中從它離開的地方繼續執行。當程式到達程式末尾時,它將終止。
這個故事的寓意是什麼?當您閱讀程式時,不要從上到下閱讀。相反,請遵循執行流程。
大多數函式需要實參,這些值控制函式的工作方式。例如,如果您想找到數字的絕對值,則必須指出該數字是什麼。Python 具有一個用於計算絕對值的內建函式
在此示例中,abs函式的實參為 5 和 -5。
有些函式接受多個實參。例如,內建函式pow接受兩個實參,底數和指數。在函式內部,傳遞的值會被分配給稱為引數的變數。
另一個接受多個實參的內建函式是max.
max可以傳送任意數量的實參(用逗號分隔),並返回傳送的最大值。實參可以是簡單值或表示式。在最後一個示例中,返回 503,因為它大於 33、125 和 1。
這是一個具有引數的使用者定義函式的示例
此函式接受一個實參並將其分配給名為param的引數。引數的值(此時我們不知道它將是什麼)被列印兩次,然後換行。名稱param是為了強化它是引數的概念而選擇的,但通常,您需要選擇一個描述引數在函式中用法的名稱。
互動式 Python shell 為我們提供了一種方便的方式來測試我們的函式。我們可以使用import 語句將我們在指令碼中定義的函式引入直譯器會話。要了解其工作原理,假設print_twice函式在名為chap03.py的指令碼中定義。我們現在可以透過將其匯入到我們的 Python shell 會話中來互動地測試它
在函式呼叫中,實參的值被分配給函式定義中相應的引數。實際上,就好像param = 'Spam'在print_twice('Spam')被呼叫時執行,param = 5在print_twice(5)中執行,以及param = 3.14159在print_twice(3.14159).
中執行。任何可以列印的實參型別都可以傳送到print_twice在第一個函式呼叫中,實參是一個字串。在第二個函式呼叫中,它是一個整數。在第三個函式呼叫中,它是一個float.
與內建函式一樣,我們可以使用表示式作為print_twice:
'Spam'*4的實參,首先計算結果為'SpamSpamSpamSpam',然後將其作為實參傳遞給
print_twice.就像數學函式一樣,Python 函式可以被組合,這意味著你可以使用一個函式的結果作為另一個函式的輸入。
在第一個例子中,abs(-7)計算結果為 7,然後成為的引數。print_twice在第二個例子中,我們有兩層組合,因為abs(-11)首先計算結果為 11,然後max(3, 1, 11, 7)計算結果為 11,並且print_twice(11)然後顯示結果。
我們也可以使用變數作為引數
請注意這裡非常重要的一點。我們作為引數傳遞的變數名(sval)與引數名(param)無關。同樣,就好像param = sval在print_twice(sval)被呼叫一樣。無論在呼叫者中該值被命名為何,在print_twice中,它的名稱是param.
在函式內部建立區域性變數時,它只存在於函式內部,你無法在函式外部使用它。例如
此函式接受兩個引數,將它們連線起來,然後列印兩次結果。我們可以用兩個字串呼叫該函式
當cat_twice終止時,變數cat被銷燬。如果我們嘗試列印它,我們會得到一個錯誤
引數也是區域性的。例如,在函式外部print_twice,不存在param這樣的東西。如果你嘗試使用它,Python 會報錯。
為了跟蹤哪些變數可以在哪裡使用,有時繪製棧圖會很有用。與狀態圖類似,棧圖顯示每個變數的值,但它們還顯示每個變數所屬的函式。
每個函式都由一個框架表示。框架是一個帶有函式名稱的框,函式的引數和變數在框內。前面示例的棧圖如下所示
棧圖棧的順序顯示了執行流程。print_twice被cat_twice中執行,以及cat_twice被__main__呼叫,這是最頂層函式的特殊名稱。當你在任何函式之外建立變數時,它屬於__main__.
每個引數都引用與其對應的引數相同的值。所以,part1與chant1, part2與chant2中執行,以及param與cat.
如果在函式呼叫期間發生錯誤,Python 會列印函式的名稱,以及呼叫它的函式的名稱,以及呼叫該函式的函式的名稱,一直回到最頂層的函式。
要檢視它是如何工作的,請建立一個名為tryme2.py的 Python 指令碼,如下所示
我們添加了語句,print cat在print_twice函式中,但是cat在此處未定義。執行此指令碼將產生如下錯誤訊息
此函式列表稱為回溯。它會告訴你錯誤發生在哪個程式檔案中,哪一行,以及當時正在執行哪些函式。它還會顯示導致錯誤的程式碼行。
請注意回溯和棧圖之間的相似性。這並非巧合。事實上,回溯的另一個常用名稱是堆疊跟蹤。
- 使用文字編輯器,建立一個名為tryme3.py在此檔案中編寫一個名為nine_lines的函式,該函式使用three_lines列印九行空白行。現在新增一個名為clear_screen的函式,該函式列印 25 行空白行。程式的最後一行應該是對clear_screen.
- 將tryme3.py的最後一行移動到程式的頂部,以便對clear_screen的函式呼叫出現在函式定義之前。執行程式並記錄您收到的錯誤訊息。您能否陳述關於函式定義和函式呼叫的規則,該規則描述了它們在程式中可以相對於彼此出現的位置?
- 從tryme3.py的工作版本開始,將new_line的定義移動到three_lines的定義之後。記錄執行此程式時發生的情況。現在將new_line的定義移動到對three_lines()的呼叫的下方。解釋這如何成為你在上一道練習中陳述的規則的一個例子。
填寫cat_n_times的函式定義的主體,以便它列印字串 s n 次
將此函式儲存在名為import_test.py現在在 Unix 提示符下,確保你在import_test.py所在的同一目錄中(ls應該顯示import_test.py)。啟動 Python shell 並嘗試以下操作
如果一切正常,你的會話應該與這個會話一樣。嘗試對cat_n_times進行其他呼叫,直到你對它的工作原理感到滿意。