函式
- 要重複使用程式,第一個步驟是函式,它是一種有名稱且獨立的程式片段
- 函式可以取用任何數量與類型的輸入參數,並回傳任何數量與類型的輸入結果
- 可以對函式做兩件事:
- 定義它
- 呼叫它
定義 Python 函式
- 輸入 def、函式名稱、用括號來框住函式的輸入參數,最後加上一個冒號(:)
- 函式名稱的命名規則與變數名稱一樣(必須以字母或_開頭,裡面只能使用字母、數字、或 _ )
先定義,並呼叫一個沒有參數的程式
- 定義時使用括號與冒號:即使函數沒有參數,還是需要在定義時使用括號與冒號
- 縮排:接下來的幾行程式都必須縮排 → 就像在 if 陳述式下面將程式縮排一樣
- 使用 pass 陳述式:Python 需要使用 pass 陳述式來說明「這個函式不會做任何事情」。它相當於 "這一頁是故意空白的"
>>> def do_nothing(): # def 函式名稱():
pass
>>> do_nothing() # 呼叫這個函式,只要輸入它的名稱與括號
>>>
定義並呼叫參數,但是會印出一個單字的函式
>>> def 函式名稱():
print('文字')
>>> 函式名稱()
文字
>>> def make_a_sound(): # 呼叫 make_a_sound()函式時,Python 會執行它的定義裡面的程式
print('quack') # 印出一個字,並返回主程式
>>> make_a_sound()
quack
沒有參數,但是會回傳一個值的函式
- 嘗試一個沒有參數,但是會回傳一個值的函式
- 可以呼叫這個函式,並使用 if 來測試它的回傳值
- 與 while 一樣,將函式與 if 及迴圈等測試式結合
>>> def 函數名稱(): # 嘗試一個沒有參數,但是會回傳一個值的函式
return True
>>> if 函數名稱(): # 呼叫這個函式,並使用 if 來測試它的回傳值
print('文字1')
else:
print('文字2')
>>> def agree():
return True
>>> if agree():
print('Splendid!')
else:
print('That was unexpected.')
Splendid!
定義函式 + 引數 + return 陳述式
- 定義函式,它有一個參數
- 它會使用 return 陳述式來將參數傳給呼叫方
- 引數:在呼叫函式時,傳給它的值
- 如果你在呼叫函式時有使用引數,這些引數的值都會被複製到函式裡面的對應參數
>>> def echo(anything): # 定義函式 echo(),它有一個「參數」,稱為 anything
return anything '' anything # 使用 return 陳述式來將 anything 傳給呼叫方兩次,並在它們之間加上空格
>>>
>>> echo('Rumplestiltskin') # 呼叫 echo(),並傳入引數字串 'Rumplestiltskin'
'Rumplestiltskin Rumplestiltskin' # 引數字串'Rumplestiltskin',這個值會被複製到 echo() 裡面的參數 anything,並回傳給呼叫方(在這裡會複製兩個,並加上空格)
>>> def commentary(color): # 寫一個函式,它會使用一個輸入引數(輸入字串參數),並且不會對它做任何實際的事情
if color == 'red':
return "It's a tomato."
elif color == 'green':
return "It's a green pepper."
elif color == 'bee purple':
return "I don't know what it is, but only bees can see it."
else:
return "I've never heard of the color " color "."
>>> comment = commentary('blue'). # 呼叫函式 commentary(),並傳入引數 'blue'
- commentary() 這個函式會做到下列的事情:
- 指派 'blue' 值給函式內部的 color 參數
- 執行 if-elif-else 邏輯鍊
- 回傳一個字串
- 指派字串給變數 comment
>>> print(comment)
I've never heard of the color blue.
函式沒有明確地呼叫 return,呼叫方會得到 None 這個結果
- 函式可以取用任何數量、任何類型的「輸入引數」(包括零)
- 它可以回傳任何數量、任何類型的「輸出結果」(包括零)
- 如果函式沒有明確地呼叫 return,呼叫方會得到 None 這個結果
>>> do_nothing()
>>> print(do_nothing())
None
None 很好用
None 當成布林計算時,被視為 false
- None 是一個特殊的 python 值,它在沒有東西可說時,保留一個位置
- 它與布林 False 不一樣,但是當你將它當成布林值來計算時,它會被視為 false
>>> 變數 = None
>>> if 變數:
print("文字1")
else:
print("文字2")
文字2
>>> thing = None
>>> if thing:
print("It's some thing")
else:
print("It's no thing")
It's no thing
區分 None 與布林值 False,可以使用 Python 的 is 運算子
- 區分 None 與布林值 False,這看起來差異不大,但在 Python 中非常重要
- 需要 None 來區分:「遺漏了值(misssing value)」與「空值(empty value)」
- 不等於 None,都是False,但它們不等於 None:零值的整數或浮點數、空字串('')、串列([])、tuple((,))、字典({})與集合(set())
>>> if 變數 is None:
print("文字1")
else:
print("文字2")
文字1
>>> if thing is None:
print("It's nothing")
else:
print("It's something")
It's nothing
>>> def is_none(thing):
if thing is None:
print("It's None")
elif thing:
print("It's True")
else:
print("It's False")
>>> is_none(None)
It's None
>>> is_none(True)
It's True
>>> is_none(False)
It's False
>>> is_none(0)
It's False
>>> is_none(0.0)
It's False
>>> is_none(())
It's False
>>> is_none([])
It's False
>>> is_none({})
It's False
>>> is_none(set())
It's False
位置引數
- 與許多語言相較之下,Python 處理函式引數的方式異常靈活
- 最常見的引數類型是位置引數,它們的值會被依序複製到對應的參數
用位置引數建立一個字典,並將它回傳
以下函式會用它的位置引數建立一個字典,並將它回傳
>>> def 變數(引數1, 引數2, 引數3):
return {'參數名稱1': 參數1, '參數名稱2': 參數2, '參數名稱3': 參數3}
>>> 變數('引數4', '引數5', '引數6')
{'參數名稱1': '引數4', '參數名稱2': '引數5', '參數名稱3': '引數6'}
>>> def menu(wine, entree, dessert):
return {'Wine': wine, 'entree': entree, 'dessert': dessert}
>>> menu('chardonnay', 'chicken', 'cake')
{'Wine': 'chardonnay', 'entree': 'chicken', 'dessert': 'cake'}
位置引數的缺點:必須記得每一個位置的意思
如果忘記,並且在呼叫menu()時,將 wine 放在最後一個引數傳入,而不是第一個,這一餐會截然不同
>>> def menu(wine, entree, dessert):
return {'Wine': wine, 'entree': entree, 'dessert': dessert}
>>> menu('beef', 'bagel', 'bordeaux')
{'Wine': 'beef', 'entree': 'bagel', 'dessert': 'bordeaux'}
關鍵字引數
避免搞不清楚引數,可以用引數的「對應參數名稱」來指定引數
- 為了避免發生避免搞不清楚引數,可以用引數的「對應參數名稱」來指定引數
- 即使它們在「函式中定義的順序」不一樣也不會產生問題
>>> def 變數(引數1, 引數2, 引數3):
return {'參數名稱1': 參數1, '參數名稱2': 參數2, '參數名稱3': 參數3}
>>> 變數(參數名稱1='引數1', 參數名稱2='引數2', 參數名稱3='引數3')
{'參數名稱1': '引數1', '參數名稱2': '引數2', '參數名稱3': '引數3'}
混合使用位置與關鍵字引數
如果呼叫函式時,要同時使用位置與關鍵字引數,位置引數必須放在前面
>>> def 變數(引數1, 引數2, 引數3):
return {'參數名稱1': 參數1, '參數名稱2': 參數2, '參數名稱3': 參數3}
>>> 變數(引數1, 參數名稱2='引數2', 參數名稱3='引數3')
{'參數名稱1': '引數1', '參數名稱2': '引數2', '參數名稱3': '引數3'}
指定預設參數值
- 可以指定預設的參數值,在呼叫方沒有提供對應的引數的時候使用
- 有時這種聽起來很平凡的功能會非常實用
- 預設的引數值會在函式被定義的時候計算,而不是執行的時候
- Python 新手經常出現的錯誤(有時稍具經驗的人也會出錯),就是使用可變的資料類型,例如串列或字典,來作為預設引數
>>> def 變數(引數1, 引數2, 引數3='參數值'):
return {'參數名稱1': 參數1, '參數名稱2': 參數2, '參數名稱3': 參數3}
>>> 變數('引數4', '引數5')
{'參數名稱1': '引數4', '參數名稱2': '引數5', '參數名稱3': '參數值'}
>>> 變數('引數6', '引數7', '引數8')
{'參數名稱1': '引數6', '參數名稱2': '引數7', '參數名稱3': '引數8'}
>>> def menu(wine, entree, dessert='pudding'):
return {'wine': wine, 'entree': entree, 'dessert': dessert}
>>> menu('chardonnay', 'chicken') # 呼叫 menu()時,不使用 dessert 引數,會使用預設參數值
{'wine': 'chardonnay', 'entree': 'chicken', 'dessert': 'pudding'}
>>> menu('dunkelfelder', 'duck', 'doughnut') # 提供引數的話,函式就會使用它,而不是預設值
{'wine': 'dunkelfelder', 'entree': 'duck', 'dessert': 'doughnut'}
【範例】函式執行 → 使用全新空字串 → 加入引數 → 印出單一項目的串列
>>> def buggy(arg, result=[]): # 預期 buggy() 函式在每次執行時,都會使用一個全新的空字串 result(bug!!)
result.append(arg) # 全新的result 空字串,對它加入 arg 引數
print(result) # 印出一個單一項目的串列
>>> buggy('a') # 串列只有對第一次呼叫函式時,是空的
['a']
>>> buggy('b') # 第二次呼叫時,result 仍然存有之前呼叫時留下來的項目
['a', 'b']
>>> def works(arg): # 如果將它寫成這樣,就可以正常動作
result = []
result.append(arg)
return result # 寫成print(result)也可以正常動作
>>> works('a')
['a']
>>> works('b')
['b']
>>> def nonbuggy(arg, result=None): # 修正方式:傳入其他的東西,來說明「函式是否第一次被呼叫」
if result is None:
result = []
result.append(arg)
print(result)
>>> nonbuggy('a')
['a']
>>> nonbuggy('b')
['b']
用 * 來收集引數
Python 沒有指標(如果你曾經使用 C 或 C++,可能會認為 Python 中的星號(*)會做指標之類的事情)
「函式的參數」使用星號 → 將「可變數量」的「潛在引數群組化」 → 變成參數值的 Tuple
>>> def print_args(*args): # 函數的參數使用星號,星號會將「可變數量」的「潛在引數群組化」,變成一個參數值的tuple
print('Positional argument tuple:', args) # args 是個參數 tuple,它是被傳入 print_args()函式的引數所產生的
>>> print_args() # 呼叫它時不傳入引數,*args 裡面就不會有任何東西
Positional argument tuple: ()
>>> print_args(3, 2, 1, 'wait!', 'uh...') # 提供引數,它會被印成 args tuple
Positional argument tuple: (3, 2, 1, 'wait!', 'uh...')
編寫 print() 這類會接收「任意數量」引數的函式 → 星號(*)這很好用
- 如果你的函式也需要用到位置引數,就把 *args 放在最後面,抓取剩下的引數
- 其實在使用 * 時,並不需要呼叫 tuple 參數 args,這種做法只是 Python 的慣例
>> def print_more(required1, required2, *args): # 函式也需要用到位置引數,就把 *args 放在最後面,抓取剩下的引數
print('Need this one:', required1)
print('Need this one too:', required2)
print('All th rest:', args)
>>> print_more('cap', 'gloves', 'scarf', 'monocle', 'mustache wax')
Need this one: cap
Need this one too: gloves
All th rest: ('scarf', 'monocle', 'mustache wax')
用 ** 來收集關鍵字引數
- 如果混合使用「位置參數」與 args 以及 *kwargs,就必須按照這個順序來排列它們
- 如同 args,其實不需要呼叫關鍵字參數 kwargs,這只是一種慣例
>>> def 變數(**kwargs): # 使用兩個星號(**)來將「關鍵字引數群組化」
print('引數名稱:', kwargs)
>>> 變數(參數名稱1='引數1', 參數名稱2='引數2', 參數名稱3='引數3')
引數名稱: {'引數名稱1: '引數1', '引數名稱2: '引數2', '引數名稱3: '引數3'}
>> def print_kwargs(**kwargs): # 定義函式 print_kwargs(),印出它的關鍵字引數
print('Keyword arguments:', kwargs) # 在函式內,kwargs 是個字典
>>> print_kwargs(wine='merlot', entree='mutton', dessert='macaroon') # 呼叫函式,使用一些關鍵字引數
Keyword arguments: {'wine': 'merlot', 'entree': 'mutton', 'dessert': 'macaroon'} # 變成一個字典,其中「引數名稱」是「鍵」,「值」是對應的「字典值」
文件字串(docstring)
- Zen of Python 可讀性必須列入考慮
- 可以將「文件」指派給「函式的定義式」→ 【做法】在函式內文的開頭加入一個字串
函式的「文件字串(docstring)」
>>> def 函式名稱(參數):
'文件字串'
return 參數
>>> def echo(anything):
'echo returns its input argument'
return anything
可以加入很長的文件字串,甚至加入豐富格式
>>> def print_if_true(thing, check):
Prints the first argument if a second argument is true.
The operation is:
1. Check whether the *second* argument is true.
2. If it is, print the *first* argument.
```
if check:
print(thing)
##印出函式的文件字串【呼叫 Python help() 函式】
```python
>>> def echo(anything):
'echo returns its input argument'
return anything
>>> help(echo) # 印出函式的文件字串,可以呼叫 Python help()函式
Help on function echo in module __main__:
echo(anything) # 傳遞「函式的名稱」來取得一串的「引數」,以及具備良好格式的文字字串
echo returns its input argument
只想看原始的文件字串,不想要有格式
doc是函式的文件字串的變數名稱(在本書 p111 "在名稱中使用與_" 會解釋使用這些底線的原因)
>>> def echo(anything):
'echo returns its input argument'
return anything
>>> print(echo.__doc__)
echo returns its input argument