Python 入門指南 5.0
單元 10 - 函數
函數 (function) 是一種功能性的程式碼集合,可以將程式 (program) 分割成小部分,藉由呼叫 (call) 函數安排執行順序
pass
定義函數使用關鍵字 (keyword) def ,其後空一格接函數名稱與小括弧,小括弧用來放參數列 (parameter list) ,函數可以有參數 (parameter) 也可以沒有參數,沒有參數的函數的小括弧留空,另外函數可用 return 設定回傳值 (return value) ,沒有回傳值的函數也就不需要 return 。舉一例如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # 計算參數總合
def my_sum(a, b):
# 回傳參數相加結果
return a + b
# 利用整數當參數
print(my_sum(33, 22))
# 利用字串當參數
print(my_sum("Hello", "World"))
# 檔名: function_demo01.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
my_sum() 函數回傳兩個參數的相加值,參數名稱及數量則是依自己定義放在參數列,這裡的參數用了 a 與 b ,另外此例用了一個 return ,這裡 return 就是函數結束執行,將控制權交還原本呼叫函數的地方
2 4 | def my_sum(a, b):
return a + b
|
此例先計算 33 與 22 的合,然後印出計算結果
9 | print(my_sum(33, 22))
|
接下來則是連接 "Hello" 與 "World" ,加號用在字串 (string) 是把兩個字串連接起來
7 | print(print(my_sum("Hello", "World"))
|
執行結果如下
$ python function_demo01.py |
55 |
HelloWorld |
$ |
函數參數可以有預設值,參數預設值是用等號在參數列直接指派數值,例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # 計算參數總合
def my_sum(a = 0, b = 0):
# 回傳參數相加結果
return a + b
# 利用關鍵字引數設定參數
print(my_sum(b = 33, a = 22))
# 不提供參數
print(my_sum())
# 檔名: function_demo02.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
這裡在參數列直接指派數值給參數 a 及 b
2 | def my_sum(a = 0, b = 0):
|
因此呼叫時可以不提供參數,會以預設值代入
9 | print(my_sum())
|
執行結果如下
$ python function_demo02.py |
55 |
0 |
$ |
呼叫函數時可以依參數位置逐一提供參數,或是用關鍵字引數 (keyword argument) 呼叫,在參數列加入斜線 / 或星號 * 等可以限制呼叫方式,或是混用兩種方式,例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | # 只能用參數位置呼叫
def my_sum1(a, b, /):
# 回傳參數相加結果
return a + b
# 只能有關鍵字引數呼叫
def my_sum2(*, a, b):
# 回傳參數相加結果
return a + b
# 兩種呼叫方式混合
def my_sum3(a, /, *, b):
# 回傳參數相加結果
return a + b
# 印出 11
print(my_sum1(5, 6))
# 印出 22
print(my_sum2(b = 11, a = 11))
# 印出 33
print(my_sum3(11, b = 22))
# 檔名: function_demo03.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
引數的英文原文為 argument ,參數的英文原文為 parameter ,所謂引數是呼叫函數時提供的實際數值,參數則是定義函數時所用的識別字名稱,兩者其實是同一件事情,之所以會有兩種名稱,這是由於英文的語言習慣是每個地方都要有專門的名詞,在此基於教學立場,除了關鍵字引數為 Python 官方文件的用詞外,其他會統稱為參數,至於引數可能會以參數值統稱。
同一件事情用不同的名詞在英文中屢見不鮮,像是在程式設計領域後面還會碰到 process 一詞, process 通常翻譯成行程,意思是正在被執行的程式,所以是程式原文 program 的進行式狀態名詞,那程式就是寫好還沒有被執行的檔案,然後詳細深入作業系統討論會嚴格把兩者區分,這裡我們在初學一率統稱 program ,也就是程式,因為要用不同詞彙去討論同一件事情的不同面向,這不是初學程式設計馬上能理解的。
斜線 / 必須放在限制位置呼叫參數的最後,例如 my_sum1() 參數列的最後是斜線 / ,表示 a 與 b 都必須以位置呼叫,也就是呼叫時提供的第一個參數為 a ,第二個為 b
2 4 | def my_sum1(a, b, /):
return a + b
|
實際呼叫就是預設的方式,但限制只能用這種方式
17 | print(my_sum1(5, 6))
|
星號 * 必須放在關鍵字引數呼叫參數的開頭,例如 my_sum2() 參數列的開頭是星號 * ,表示 a 與 b 都必須以關鍵字引數呼叫,也就是呼叫時必須以參數名稱設定
7 9 | def my_sum1(a, b, /):
return a + b
|
實際呼叫限制使用關鍵字呼叫,呼叫的順序則沒有限制
19 | print(my_sum2(b = 11, a = 11))
|
如果參數列定義沒有加上斜線 / 或星號 * ,就可以混用位置呼叫或是關鍵字引數呼叫,但是一樣位置呼叫在前,關鍵字引數呼叫在後。
兩者標記也可以混用,斜線 / 之前限制位置呼叫,星號 * 之後為關鍵字引數呼叫
12 14 | def my_sum3(a, /, *, b):
return a + b
|
位置呼叫直接提供參數值,關鍵字引數呼叫則要加上參數名稱與等號
21 | print(my_sum3(11, b = 22))
|
執行結果如下
$ python function_demo03.py |
11 |
22 |
33 |
$ |
參數數量也可以不受限制,在參數識別字前加上一個星號 * 表示參數為序對 (tuple) ,要以位置參數呼叫,加上兩個星號表示字典 (dictionary) ,要以關鍵字引數呼叫,例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | # 不限個數參數的序對版本
def my_sum1(*arg):
# 印出參數型態
print(type(arg))
# 暫存回傳結果
result = 0
# 依參數計算總合
for i in arg:
result += i
# 回傳參數總合
return result
# 不限個數參數的字典版本
def my_sum2(**arg):
# 印出參數型態
print(type(arg))
# 暫存回傳結果
result = 0
# 依參數計算總合
for i in arg:
result += arg[i]
# 回傳參數總合
return result
# 利用位置提供參數
print(my_sum1(11, 22, 33))
# 利用關鍵字引數設定參數
print(my_sum2(a = 11, b = 22, c = 33))
# 檔名: function_demo04.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
my_sum1() 與 my_sum2() 的內容幾乎相同,都是先用內建函數 type() 印出參數的型態 (type) ,然後利用 for 迴圈 (loop) 替參數加總,最後回傳參數的所有相加值,處理上除了 my_sum1() 是序對外, my_sum2() 由於是字典,所以 for 迴圈的 i 是取得 key
20 21 | for i in arg:
result += arg[i]
|
呼叫時關鍵字引數中等號左邊的關鍵字會是 key
26 28 | print(my_sum1(11, 22, 33))
print(my_sum2(a = 11, b = 22, c = 33))
|
執行結果如下
$ python function_demo04.py |
<class 'tuple'> |
66 |
<class 'dict'> |
66 |
$ |
關鍵字 lambda 可以定義簡單的運算式 (expression) ,在函數需要以函數當作參數時,可以替代函數函數當作參數,以下簡單示範使用 lambda
1 2 3 4 5 6 7 8 9 10 | # 變數 sum 是 lambda 定義的無名函數
my_sum = lambda x, y: x + y
# 印出 33
print(my_sum(11, 22))
# 檔名: function_demo05.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
此例 lambda 的定義指派給變數 my_sum , my_sum 就變成需要參數才能使用的函數
2 | my_sum = lambda x, y: x + y
|
lambda 定義出的運算式也被稱為無名函數 (anonymous function) 。
執行結果如下
$ python function_demo05.py |
33 |
$ |
關鍵字 yield 用在函數中可以定義產生器 (generator) 函數,例如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # 定義產生器函數
def number(*arg):
for i in range(len(arg)):
# 依次產生引數值
yield arg[i]
# 印出產生器函數的計算結果
print(sum(number(11, 22, 33)))
# 印出產生器運算式的計算結果
print(sum(i for i in [11, 22, 33]))
# 檔名: function_demo06.py
# 說明:《Python入門指南》的範例程式
# 網站: http://kaiching.org
# 作者: 張凱慶
# 時間: 2023 年 5 月
|
所謂產生器就是依序產生數值的意思,至於產生數值的規則可以自訂,此例是依序讓 number() 產生序對中的參數值
2 3 5 | def number(*arg):
for i in range(len(arg)):
yield arg[i]
|
底下用內建函數 sum() 對參數進行加總,注意第 8 行是由產生器函數產生的數值
8 | print(sum(number(11, 22, 33)))
|
第 10 行則是產生器運算式 (generator expression) 產生的數值
10 | print(sum(i for i in [11, 22, 33]))
|
關鍵字 for 之前的變數 i 就會產生的數值,此變數 i 就是由 for 在關鍵字 in 後面取得的串列元素,這裡是整數,因此會依次產生 11 、 22 、 33 供 sum() 計算。
產生器的目的是產生數列中的數值,產生器運算式是簡單的產生器寫法,用 for 迴圈直接在複合資料型態 (compound data type) 中取得數值,就是相對數值已經建立好,或是還需額外放在函數中計算,總之是產生器函數的語法糖,而產生器其實也是迭代器 (iterator) 的語法糖,我們會在單元 14 介紹迭代器。
上例執行結果如下
$ python function_demo06.py |
66 |
66 |
$ |
函數是模組化 (modular design) 程式設計的第一步,繼續模組化程式設計就是要來介紹類別 (class) ,不過在進入類別之前,我們先來進一步討論一下指派運算 (assignment expression) 。
中英文術語對照 | |
---|---|
無名函數 | anonymous function |
指派運算 | assignment expression |
呼叫 | call |
複合資料型態 | compound data type |
類別 | class |
字典 | dictionary |
運算式 | expression |
函數 | function |
產生器 | generator |
產生器運算式 | generator expression |
迭代器 | iterator |
關鍵字 | keyword |
關鍵字引數 | keyword argument |
迴圈 | loop |
模組化 | modular design |
參數列 | parameter list |
參數 | parameter |
程式 | program |
回傳值 | return value |
序對 | tuple |
型態 | type |
重點整理 |
---|
1. 函數是一種功能性的程式碼集合,可以有參數及回傳值,參數不限個數,回傳值最多只能有一個。 |
2. return 陳述將執行控制權還給呼叫函數的地方。 |
3. 如果要給函數的參數設定預設值,就直接在參數列用等號指派數值,呼叫時就可以不提供參數。 |
4. 呼叫函數提供參數時,可以依照參數列順序或是利用關鍵字引數,如果要限制呼叫方式會混用兩者,需要借助 / 或 * 。 |
5. 參數可以不限制數量,在參數前加上星號會取得序對,並限制依順序呼叫,若是加上兩個星號表示字典,限制用關鍵字引數呼叫。 |
6. lambda 運算式可以定義簡單的無名函數。 |
7. 函數中用 yield 而非 return 就是產生器函數。 |
8. 產生器運算式是產生器函數的簡化寫法。 |
問題與討論 |
---|
1. 為什麼函數要有參數?若在函數中改變參數的值,原本的參數會被改變嗎? |
2. 為什麼函數只能有一個回傳值?如果有需要回傳多個數值該如何處理? |
3. 要怎麼替函數的參數設定預設值? |
4. 要怎麼限制函數的參數要按照順序提供?或是限制使用關鍵字引數? |
5. 什麼是無名函數?為什麼要使用無名函數? |
5. 什麼是產生器函數? |
練習 |
---|
1. 寫一個程式 exercise1001.py ,裡頭設計一個函數 my_sum() ,用以計算兩個整數的和。 參考程式碼 |
2. 承上題,另寫一個程式 exercise1002.py ,改成接受使用者輸入的版本。 參考程式碼 |
3. 寫一個程式 exercise1003.py ,裡頭設計一個函數 my_sum() ,只用一個整數參數 p ,結果回傳 1 到 p 之間所有正整數的和。 參考程式碼 |
4. 承上題,另寫一個程式 exercise1004.py ,改成接受使用者輸入的版本。 參考程式碼 |
5. 寫一個程式 exercise1005.py ,裡頭設計一個函數 factorial() ,用以計算階乘值。 參考程式碼 |
6. 承上題,另寫一個程式 exercise1006.py ,改成接受使用者輸入的版本。 參考程式碼 |
7. 寫一個程式 exercise1007.py ,裡頭設計一個函數 fibonacci() ,用以計算費氏數列。 參考程式碼 |
8. 承上題,另寫一個程式 exercise1008.py ,改成接受使用者輸入的版本。 參考程式碼 |
9. 寫一個程式 exercise1009.py ,設計印出時間的 print_time() 函數,參數 now 的值預設為 None ,並在函數中確認 None 有值,如果沒有值就指派為現在時間。 參考程式碼 |
10. 寫一個程式 exercise1010.py ,設計一個至少需要三個參數的函數,例如 do_something() ,然後在函數內依據函數的值回傳計算結果,計算方式請自行定義,最後利用關鍵字引數呼叫並印出回傳值。 參考程式碼 |
11. 承上題,將程式寫在 exercise1011.py 中,將參數改為限制位置呼叫。 參考程式碼 |
12. 承上題,將程式寫在 exercise1012.py 中,將參數改為限制用關鍵數引數呼叫。 參考程式碼 |
13. 承上題,將程式寫在 exercise1013.py 中,將參數改為混用位置與關鍵數引數呼叫。 參考程式碼 |
14. 寫一個程式 exercise1014.py ,設計函數採用序對的不限個數參數,然後在函數中印出每個參數。 參考程式碼 |
15. 承上題,將程式寫在 exercise1015.py 中,改用字典的不限個數參數,然後在函數中增加新的 key:value ,例如 a["z"] = "end" ,最後回傳參數 a ,然後在底下執行部分印出每個 key 的 value 。 參考程式碼 |
16. 關鍵字 lambda 通常會用在需要函數當作參數的場合,將程式寫在 exercise1016.py 中,,設計一個具有兩個序對組合的串列,例如 [(12, "A01"), (9, "B33"), (2, "C02")] ,然後利用內建函數 sorted() 進行排序,用 lambda 設定關鍵字引數 key ,使其用序對中的第二個元素進行排序。 參考程式碼 |
17. 承練習 5 ,將新程式寫在 exercise1017.py 中,把計算階乘的函數改為產生器函數。 參考程式碼 |
18. 承練習 7 ,將新程式寫在 exercise1018.py 中,把計算費氏數列的函數改為產生器函數。 參考程式碼 |