### 4.2.4 小結:函數的定義與調用
通過前面的例子,讀者應該已經非常熟悉 Python 中函數定義的語法。在此總結如下:
```
def <函數名>(<形式參數>):
<函數體>
```
其中函數名是標識符,命名必須符合 Python 標識符的規定;形式參數是用逗號分隔的變量名序列(可以為空)。函數體是語句序列,左端必須縮進一些空白。 一旦定義了一個函數,就可以在程序的任何地方調用這個函數。函數調用的語法如下:
```
<函數名>(<實際參數>)
```
其中實際參數可以是表達式,個數必須和形式參數相同。注意,這里列出的函數調用語法實 際上適用于沒有返回值的函數,即 4.1.3 節中提到的“過程”。4.2.6 小節會討論具有返回值 的函數。
當 Python 遇到一個函數調用時,將通過四個步驟來處理這個調用。假設程序 P 現在執 行到了一個函數調用 f(a),則這四個步驟是:
(1)調用者 P 在調用點暫停執行(術語也稱為 P 掛起);
(2)函數 f 的形式參數被賦予實際參數 a 的值;
(3)執行 f 的函數體;
(4)f 執行完畢后,控制返回到 P 中調用點的下一條語句。
下面我們以程序 4.6 為例,具體描述函數調用過程。為了方便閱讀,將程序 4.6 的主函 數 main 羅列在下面,整個程序從 main 開始執行。
```
def main():
star_treetop()
treetrunk()
print
caret_treetop()
treetrunk()
```
當 Python 執行到 star_treetop()時,main 暫停執行,控制轉到 star_treetop。 因為沒有參數傳遞問題,所以直接執行 star_treetop 的函數體。圖 4.1 描述了這個函數 調用的控制轉移情況。

圖 4.1 控制從 main 轉移到 star_treetop
控制轉到 star_treetop 后執行的第一條語句又是一個函數調用 treetop("*"), 于是 Python 又暫停執行 star_treetop,而將控制轉到 treetop("*")。Python 檢查 treetop 的定義后發現它有一個形式參數 ch,于是將函數調用 treetop("*")的實際參 數"*"傳遞給形式參數 ch,這相當于在 treetop 的函數體之前增加了一條賦值語句:
```
ch = "*"
```
參數傳遞后開始執行 treetop 的函數體。圖 4.2 展現了這時的狀態,注意 treetop內部的變量 ch 已經被賦值為"*"。

圖 4.2 控制從 star_treetop 轉移到 treetop
由于 treetop()的函數體是一系列 print 語句,沒有更多函數調用,于是 Python 順 序執行這些語句,結束后將控制返回到 treetop 調用點的下一條語句,即 star_treetop 中的第二條 treetop("*")語句,這時的情形參看圖 4.3。注意,當函數執行完畢,函數的 變量所占用的存儲空間將被 Python 收回,任何變量都不可能將數據保持到下一次執行函數, 故圖 4.3 中 ch 顯示為未賦值狀態。

圖 4.3 控制從 treetop 返回 star_treetop
接下來執行 star_treetop 的第二條 treetop("*"),其過程和前面一條完全一樣, 我們就不作圖演示了。現在,當控制從 treetop 再次返回 star_treetop 時,此函數也 執行完畢,故控制又返回到 main 函數中調用點的下一條語句。如圖 4.4 所示。

圖 4.4 控制從 star_treetop 返回 main
控制返回 main 后執行的是第二條語句 treetrunk(),這又是一個函數調用。于是 main 再次暫停執行,控制轉移到函數 treetrunk。treetrunk 執行完畢控制返回 main, 執行第三條語句 print,輸出一個空行后執行函數調用 caret_treetop()。這和前面 star_treetop()的執行過程是類似的,控制轉移到 caret_treetop 的函數體后遇到的 是 treetop("^"),這次傳遞給形式參數 ch 的值是字符"^",圖 4.5 表示了此時的狀態。

圖 4.5 控制從 caret_treetop 轉到 treetop 并傳遞不同實際參數
此后的執行過程與上述類似,我們不再逐一說明。當程序最后一行的調用 treetrunk 執行完畢,控制返回到 main 時到達程序末尾,于是整個程序結束。其實,main 本身也是一個函數,程序 4.6 的最后一行就是對 main 的調用。由于 main 是頂層模塊,調用并執行 main 后控制只能返回給 Python——所以整個程序執行完畢后我們看到的是熟悉的“>>>”。
以上我們通過例子描述了 Python 的函數定義和調用。還要說明一點,函數定義中提到 形式參數可以是用逗號分隔的變量名序列。對于有多個形式參數的函數,調用時一定要注意 形式參數與實際參數的匹配。簡單的做法是按位置匹配,即調用時提供的第一個實際參數賦 值給第一個形式參數,第二個實際參數賦值給第二個形式參數,依此類推。
作為例子,我們再來研究用字符畫樹冠的問題。樹冠是由兩個三角形圖案組成的,程序 4.2 或程序 4.6 中,函數 treetop 的功能就是用字符畫三角形圖案,只不過程序 4.2 固定用 字符"*"畫畫,程序 4.6 可以用任意字符畫畫。觀察 treetop 的函數體,可見圖案是由多 條 print 語句所打印的字符串拼成的,并且每條 print 所打印的字符串很有規律:每行中 "*"的個數是自頂向下分別是 1、3、5、7,而左邊留的空格數自頂向下分別是 3、2、1、0。 對這些數字做一點分析,很容易得出規律:設樹冠最寬處有 w 個"*"字符,則當某一行上要 畫 c 個"*"時,該行左邊留的空格數就是(w - c) / 2。根據這個規律,我們定義一個新的 treetop 函數,它具有兩個參數:一個是畫圖所用字符 ch,另一個是樹冠寬度 width(為 對稱起見應該用奇數,此前例子都固定為 7)。顯然這個新的 treetop 函數更加通用化,可 以用任意字符畫任意寬度的樹冠。
```
def treetop(ch,width):
for c in range(1,width+1,2):
print ((width–c)/2) * " " + c * ch
```
下面我們在 Python 交互環境下定義這個函數,然后做一些測試。結果如下:
```
>>> treetop("*",7)
*
***
*****
*******
>>> treetop("@",9)
@
@@@
@@@@@
@@@@@@@
@@@@@@@@@
>>> treetop(11,"A")
Traceback (most recent call last):
File "<pyshell#9>", line 1, in <module> treetop(11,"A")
File "<pyshell#2>", line 2, in treetop for c in range(1,width+1,2):
TypeError: cannot concatenate 'str' and 'int' objects
```
從上例可知,由于函數 treetop 有兩個形式參數,因此調用該函數時必須傳遞兩個實 際參數與之匹配。參數傳遞的效果相當于在 treetop 的函數體前面執行了兩條賦值語句:
```
ch = ...
width = ...
```
如果實際參數與形式參數不匹配,函數執行就可能出錯,如上例中的 treetop(11,"A")。 更嚴重的是函數執行似乎沒有出錯,但參數的錯誤匹配實際上導致計算結果完全沒有意義。
例如我們定義一個顯示身高體重信息的函數,然后調用之:
```
>>> def printInfo(height,weight):
print "Height:",height
print "Weight:",weight
>>> printInfo(80,1.80)
Height: 80
Weight: 1.8
```
可見,由于調用時參數傳遞不匹配,函數雖然能夠執行,但結果無意義。
關鍵字參數
函數調用時的參數傳遞通常采用上述“按位置匹配”的方式,但 Python 還提供另一種 參數傳遞方式——關鍵字參數。關鍵字參數形如“<形參名> = <實參值>”,即通過形式參數 的名字來指示為哪個形參傳遞什么值。例如:
```
>>> treetop(width = 11,ch = "A")
A
AAA
AAAAA
AAAAAAA
AAAAAAAAA
AAAAAAAAAAA
```
關鍵字參數在某些場合用起來更方便。例如,如果一個函數有很多參數,但是調用時只 想為個別參數傳遞值,而其他參數采用缺省值,這是采用關鍵字參數就是必然的選擇。下面 是一個簡單的例子:
```
>>> def f(a,b=7,c=2):
print a,b,c
>>> f(2005)
2005 7 2
>>> f(1927,8,1)
1927 8 1
>>> f(1921,c=1)
1921 7 1
```
注意,這個例子同時說明了如何為函數參數指定缺省值。
- 前言
- 第 1 章 計算與計算思維
- 1.1 什么是計算?
- 1.1.1 計算機與計算
- 1.1.2 計算機語言
- 1.1.3 算法
- 1.1.4 實現
- 1.2 什么是計算思維?
- 1.2.1 計算思維的基本原則
- 1.2.2 計算思維的具體例子
- 1.2.3 日常生活中的計算思維
- 1.2.4 計算思維對其他學科的影響
- 1.3 初識 Python
- 1.3.1 Python 簡介
- 1.3.2 第一個程序
- 1.3.3 程序的執行方式
- 1.3.4 Python 語言的基本成分
- 1.4 程序排錯
- 1.5 練習
- 第 2 章 用數據表示現實世界
- 2.1 數據和數據類型
- 2.1.1 數據是對現實的抽象
- 2.1.1 常量與變量
- 2.1.2 數據類型
- 2.1.3 Python 的動態類型*
- 2.2 數值類型
- 2.2.1 整數類型 int
- 2.2.2 長整數類型 long
- 2.2.3 浮點數類型 float
- 2.2.4 數學庫模塊 math
- 2.2.5 復數類型 complex*
- 2.3 字符串類型 str
- 2.3.1 字符串類型的字面值形式
- 2.3.2 字符串類型的操作
- 2.3.3 字符的機內表示
- 2.3.4 字符串類型與其他類型的轉換
- 2.3.5 字符串庫 string
- 2.4 布爾類型 bool
- 2.4.1 關系運算
- 2.4.2 邏輯運算
- 2.4.3 布爾代數運算定律*
- 2.4.4 Python 中真假的表示與計算*
- 2.5 列表和元組類型
- 2.5.1 列表類型 list
- 2.5.2 元組類型 tuple
- 2.6 數據的輸入和輸出
- 2.6.1 數據的輸入
- 2.6.2 數據的輸出
- 2.6.3 格式化輸出
- 2.7 編程案例:查找問題
- 2.8 練習
- 第 3 章 數據處理的流程控制
- 3.1 順序控制結構
- 3.2 分支控制結構
- 3.2.1 單分支結構
- 3.2.2 兩路分支結構
- 3.2.3 多路分支結構
- 3.3 異常處理
- 3.3.1 傳統的錯誤檢測方法
- 3.3.2 傳統錯誤檢測方法的缺點
- 3.3.3 異常處理機制
- 3.4 循環控制結構
- 3.4.1 for 循環
- 3.4.2 while 循環
- 3.4.3 循環的非正常中斷
- 3.4.4 嵌套循環
- 3.5 結構化程序設計
- 3.5.1 程序開發過程
- 3.5.2 結構化程序設計的基本內容
- 3.6 編程案例:如何求 n 個數據的最大值?
- 3.6.1 幾種解題策略
- 3.6.2 經驗總結
- 3.7 Python 布爾表達式用作控制結構*
- 3.8 練習
- 第 4 章 模塊化編程
- 4.1 模塊化編程基本概念
- 4.1.1 模塊化設計概述
- 4.1.2 模塊化編程
- 4.1.3 編程語言對模塊化編程的支持
- 4.2 Python 語言中的函數
- 4.2.1 用函數減少重復代碼 首先看一個簡單的用字符畫一棵樹的程序:
- 4.2.2 用函數改善程序結構
- 4.2.3 用函數增強程序的通用性
- 4.2.4 小結:函數的定義與調用
- 4.2.5 變量的作用域
- 4.2.6 函數的返回值
- 4.3 自頂向下設計
- 4.3.1 頂層設計
- 4.3.2 第二層設計
- 4.3.3 第三層設計
- 4.3.4 第四層設計
- 4.3.5 自底向上實現與單元測試
- 4.3.6 開發過程小結
- 4.4 Python 模塊*
- 4.4.1 模塊的創建和使用
- 4.4.2 Python 程序架構
- 4.4.3 標準庫模塊
- 4.4.4 模塊的有條件執行
- 4.5 練習
- 第 5 章 圖形編程
- 5.1 概述
- 5.1.1 計算可視化
- 5.1.2 圖形是復雜數據
- 5.1.3 用對象表示復雜數據
- 5.2 Tkinter 圖形編程
- 5.2.1 導入模塊及創建根窗口
- 5.2.2 創建畫布
- 5.2.3 在畫布上繪圖
- 5.2.4 圖形的事件處理
- 5.3 編程案例
- 5.3.1 統計圖表
- 5.3.2 計算機動畫
- 5.4 軟件的層次化設計:一個案例
- 5.4.1 層次化體系結構
- 5.4.2 案例:圖形庫 graphics
- 5.4.3 graphics 與面向對象
- 5.5 練習
- 第 6 章 大量數據的表示和處理
- 6.1 概述
- 6.2 有序的數據集合體
- 6.2.1 字符串
- 6.2.2 列表
- 6.2.3 元組
- 6.3 無序的數據集合體
- 6.3.1 集合
- 6.3.2 字典
- 6.4 文件
- 6.4.1 文件的基本概念
- 6.4.2 文件操作
- 6.4.3 編程案例:文本文件分析
- 6.4.4 緩沖
- 6.4.5 二進制文件與隨機存取*
- 6.5 幾種高級數據結構*
- 6.5.1 鏈表
- 6.5.2 堆棧
- 6.5.3 隊列
- 6.6 練習
- 第 7 章 面向對象思想與編程
- 7.1 數據與操作:兩種觀點
- 7.1.1 面向過程觀點
- 7.1.2 面向對象觀點
- 7.1.3 類是類型概念的發展
- 7.2 面向對象編程
- 7.2.1 類的定義
- 7.2.2 對象的創建
- 7.2.3 對象方法的調用
- 7.2.4 編程實例:模擬炮彈飛行
- 7.2.5 類與模塊化
- 7.2.6 對象的集合體
- 7.3 超類與子類*
- 7.3.1 繼承
- 7.3.2 覆寫
- 7.3.3 多態性
- 7.4 面向對象設計*
- 7.5 練習
- 第 8 章 圖形用戶界面
- 8.1 圖形用戶界面概述
- 8.1.1 程序的用戶界面
- 8.1.2 圖形界面的組成
- 8.1.3 事件驅動
- 8.2 GUI 編程
- 8.2.1 UI 編程概述
- 8.2.2 初識 Tkinter
- 8.2.3 常見 GUI 構件的用法
- 8.2.4 布局
- 8.2.5 對話框*
- 8.3 Tkinter 事件驅動編程
- 8.3.1 事件和事件對象
- 8.3.2 事件處理
- 8.4 模型-視圖設計方法
- 8.4.1 將 GUI 應用程序封裝成對象
- 8.4.2 模型與視圖
- 8.4.3 編程案例:匯率換算器
- 8.5 練習
- 第 9 章 模擬與并發
- 9.1 模擬
- 9.1.1 計算機建模
- 9.1.2 隨機問題的建模與模擬
- 9.1.3 編程案例:乒乓球比賽模擬
- 9.2 原型法
- 9.3 并行計算*
- 9.3.1 串行、并發與并行
- 9.3.2 進程與線程
- 9.3.3 多線程編程的應用
- 9.3.4 Python 多線程編程
- 9.3.5 小結
- 9.4 練習
- 第 10 章 算法設計和分析
- 10.1 枚舉法
- 10.2 遞歸
- 10.3 分治法
- 10.4 貪心法
- 10.5 算法分析
- 10.5.1 算法復雜度
- 10.5.2 算法分析實例
- 10.6 不可計算的問題
- 10.7 練習
- 第 11 章 計算+X
- 11.1 計算數學
- 11.2 生物信息學
- 11.3 計算物理學
- 11.4 計算化學
- 11.5 計算經濟學
- 11.6 練習
- 附錄
- 1 Python 異常處理參考
- 2 Tkinter 畫布方法
- 3 Tkinter 編程參考
- 3.1 構件屬性值的設置
- 3.2 構件的標準屬性
- 3.3 各種構件的屬性
- 3.4 對話框
- 3.5 事件
- 參考文獻