在 Python 中,你也可以定義包含若干參數的函數。這里有三種可用的形式,也可以混合使用。
### 4.7.1\. 默認參數值
最常用的一種形式是為一個或多個參數指定默認值。這會創建一個可以使用比定義時允許的參數更少的參數調用的函數,例如:
~~~
def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
while True:
ok = input(prompt)
if ok in ('y', 'ye', 'yes'):
return True
if ok in ('n', 'no', 'nop', 'nope'):
return False
retries = retries - 1
if retries 0:
raise IOError('refusenik user')
print(complaint)
~~~
這個函數可以通過幾種不同的方式調用:
* 只給出必要的參數:?ask_ok('Do?you?really?want?to?quit?')
* 給出一個可選的參數:?ask_ok('OK?to?overwrite?the?file?',?2)
* 或者給出所有的參數:?ask_ok('OK?to?overwrite?the?file?',?2,?'Come?on,?only?yes?or?no!')
這個例子還介紹了?in?關鍵字。它測定序列中是否包含某個確定的值。
默認值在函數?_定義_?作用域被解析,如下所示:
~~~
i = 5
def f(arg=i):
print(arg)
i = 6
f()
~~~
將會輸出?5。
**重要警告:**?默認值只被賦值一次。這使得當默認值是可變對象時會有所不同,比如列表、字典或者大多數類的實例。例如,下面的函數在后續調用過程中會累積(前面)傳給它的參數:
~~~
def f(a, L=[]):
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
~~~
這將輸出:
[1]
[1, 2]
[1, 2, 3]
如果你不想讓默認值在后續調用中累積,你可以像下面一樣定義函數:
~~~
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
~~~
### 4.7.2\. 關鍵字參數
函數可以通過?_關鍵字參數_?的形式來調用,形如?keyword?=?value。例如,以下的函數:
~~~
def parrot(voltage, state='a stiff', action='voom', type='Norwegian Blue'):
print("-- This parrot wouldn't", action, end=' ')
print("if you put", voltage, "volts through it.")
print("-- Lovely plumage, the", type)
print("-- It's", state, "!")
~~~
接受一個必選參數 (voltage) 以及三個可選參數 (state,?action, 和?type)。可以用以下的任一方法調用:
~~~
parrot(1000) # 1 positional argument
parrot(voltage=1000) # 1 keyword argument
parrot(voltage=1000000, action='VOOOOOM') # 2 keyword arguments
parrot(action='VOOOOOM', voltage=1000000) # 2 keyword arguments
parrot('a million', 'bereft of life', 'jump') # 3 positional arguments
parrot('a thousand', state='pushing up the daisies') # 1 positional, 1 keyword
~~~
不過以下幾種調用是無效的:
~~~
parrot() # required argument missing
parrot(voltage=5.0, 'dead') # non-keyword argument after a keyword argument
parrot(110, voltage=220) # duplicate value for the same argument
parrot(actor='John Cleese') # unknown keyword argument
~~~
通常,參數列表必須(先書寫)位置參數然后才是關鍵字參數,這里關鍵字必須來自于形參名字。形參是否有一個默認值并不重要。任何參數都不能被多次賦值——在同一個調用中,與位置參數相同的形參名字不能用作關鍵字。這里有一個違反此限制而出錯的例子:
~~~
>>> def function(a):
... pass
...
>>> function(0, a=0)
Traceback (most recent call last):
File "", line 1, in ?
TypeError: function() got multiple values for keyword argument 'a'
~~~
引入一個形如?**name?的參數時,它接收一個字典(參見?_typesmapping_?),該字典包含了所有未出現在形式參數列表中的關鍵字參數。這里可能還會組合使用一個形如?*name?(下一小節詳細介紹) 的形式參數,它接收一個元組(下一節中會詳細介紹),包含了所有沒有出現在形式參數列表中的參數值(?*name?必須在?**name?之前出現)。 例如,我們這樣定義一個函數:
~~~
def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
keys = sorted(keywords.keys())
for kw in keys:
print(kw, ":", keywords[kw])
~~~
它可以像這樣調用:
~~~
cheeseshop("Limburger", "It's very runny, sir.",
"It's really very, VERY runny, sir.",
shopkeeper="Michael Palin",
client="John Cleese",
sketch="Cheese Shop Sketch")
~~~
當然它會按如下內容打印:
~~~
-- Do you have any Limburger ?
-- I'm sorry, we're all out of Limburger
It's very runny, sir.
It's really very, VERY runny, sir.
----------------------------------------
client : John Cleese
shopkeeper : Michael Palin
sketch : Cheese Shop Sketch
~~~
注意在打印?關鍵字?參數字典的內容前先調用 sort() 方法。否則的話,打印參數時的順序是未定義的。
### 4.7.3\. 可變參數列表
最后,一個最不常用的選擇是可以讓函數調用可變個數的參數。這些參數被包裝進一個元組(參見[_元組和序列_](http://www.pythondoc.com/pythontutorial3/datastructures.html#tut-tuples)?)。在這些可變個數的參數之前,可以有零到多個普通的參數:
~~~
def write_multiple_items(file, separator, *args):
file.write(separator.join(args))
~~~
通常,這些?可變?參數是參數列表中的最后一個,因為它們將把所有的剩余輸入參數傳遞給函數。任何出現在?*args?后的參數是關鍵字參數,這意味著,他們只能被用作關鍵字,而不是位置參數:
~~~
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
~~~
### 4.7.4\. 參數列表的分拆
另有一種相反的情況: 當你要傳遞的參數已經是一個列表,但要調用的函數卻接受分開一個個的參數值。這時候你要把已有的列表拆開來。例如內建函數?range()?需要要獨立的?_start_,_stop_?參數。你可以在調用函數時加一個?*?操作符來自動把參數列表拆開:
~~~
>>> list(range(3, 6)) # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> list(range(*args)) # call with arguments unpacked from a list
[3, 4, 5]
~~~
以同樣的方式,可以使用?**?操作符分拆關鍵字參數為字典:
~~~
>>> def parrot(voltage, state='a stiff', action='voom'):
... print("-- This parrot wouldn't", action, end=' ')
... print("if you put", voltage, "volts through it.", end=' ')
... print("E's", state, "!")
...
>>> d = {"voltage": "four million", "state": "bleedin' demised", "action": "VOOM"}
>>> parrot(**d)
-- This parrot wouldn't VOOM if you put four million volts through it. E's bleedin' demised !
~~~
### 4.7.5\. Lambda 形式
出于實際需要,有幾種通常在函數式編程語言例如 Lisp 中出現的功能加入到了 Python。通過lambda?關鍵字,可以創建短小的匿名函數。這里有一個函數返回它的兩個參數的和:lambda?a,?b:?a+b。 Lambda 形式可以用于任何需要的函數對象。出于語法限制,它們只能有一個單獨的表達式。語義上講,它們只是普通函數定義中的一個語法技巧。類似于嵌套函數定義,lambda 形式可以從外部作用域引用變量:
~~~
>>> def make_incrementor(n):
... return lambda x: x + n
...
>>> f = make_incrementor(42)
>>> f(0)
42
>>> f(1)
43
~~~
### 4.7.6\. 文檔字符串
這里介紹的文檔字符串的概念和格式。
第一行應該是關于對象用途的簡介。簡短起見,不用明確的陳述對象名或類型,因為它們可以從別的途徑了解到(除非這個名字碰巧就是描述這個函數操作的動詞)。這一行應該以大寫字母開頭,以句號結尾。
如果文檔字符串有多行,第二行應該空出來,與接下來的詳細描述明確分隔。接下來的文檔應該有一或多段描述對象的調用約定、邊界效應等。
Python 的解釋器不會從多行的文檔字符串中去除縮進,所以必要的時候應當自己清除縮進。這符合通常的習慣。第一行之后的第一個非空行決定了整個文檔的縮進格式。(我們不用第一行是因為它通常緊靠著起始的引號,縮進格式顯示的不清楚。)留白“相當于”是字符串的起始縮進。每一行都不應該有縮進,如果有縮進的話,所有的留白都應該清除掉。留白的長度應當等于擴展制表符的寬度(通常是8個空格)。
以下是一個多行文檔字符串的示例:
~~~
>>> def my_function():
... """Do nothing, but document it.
...
... No, really, it doesn't do anything.
... """
... pass
...
>>> print(my_function.__doc__)
Do nothing, but document it.
No, really, it doesn't do anything.
~~~
- Python 入門指南
- 1. 開胃菜
- 2. 使用 Python 解釋器
- 2.1. 調用 Python 解釋器
- 2.2. 解釋器及其環境
- 3. Python 簡介
- 3.1. 將 Python 當做計算器
- 3.2. 編程的第一步
- 4. 深入 Python 流程控制
- 4.1. if 語句
- 4.2. for 語句
- 4.3. range() 函數
- 4.4. break 和 continue 語句, 以及循環中的 else 子句
- 4.5. pass 語句
- 4.6. 定義函數
- 4.7. 深入 Python 函數定義
- 4.8. 插曲:編碼風格
- 5. 數據結構
- 5.1. 關于列表更多的內容
- 5.2. del 語句
- 5.3. 元組和序列
- 5.4. 集合
- 5.5. 字典
- 5.6. 循環技巧
- 5.7. 深入條件控制
- 5.8. 比較序列和其它類型
- 6. 模塊
- 6.1. 深入模塊
- 6.2. 標準模塊
- 6.3. dir() 函數
- 6.4. 包
- 7. 輸入和輸出
- 7.1. 格式化輸出
- 7.2. 文件讀寫
- 8. 錯誤和異常
- 8.1. 語法錯誤
- 8.2. 異常
- 8.3. 異常處理
- 8.4. 拋出異常
- 8.5. 用戶自定義異常
- 8.6. 定義清理行為
- 8.7. 預定義清理行為
- 9. 類
- 9.1. 術語相關
- 9.2. Python 作用域和命名空間
- 9.3. 初識類
- 9.4. 一些說明
- 9.5. 繼承
- 9.6. 私有變量
- 9.7. 補充
- 9.8. 異常也是類
- 9.9. 迭代器
- 9.10. 生成器
- 9.11. 生成器表達式
- 10. Python 標準庫概覽
- 10.1. 操作系統接口
- 10.2. 文件通配符
- 10.3. 命令行參數
- 10.4. 錯誤輸出重定向和程序終止
- 10.5. 字符串正則匹配
- 10.6. 數學
- 10.7. 互聯網訪問
- 10.8. 日期和時間
- 10.9. 數據壓縮
- 10.10. 性能度量
- 10.11. 質量控制
- 10.12. “瑞士軍刀”
- 11. 標準庫瀏覽 – Part II
- 11.1. 輸出格式
- 11.2. 模板
- 11.3. 使用二進制數據記錄布局
- 11.4. 多線程
- 11.5. 日志
- 11.6. 弱引用
- 11.7. 列表工具
- 11.8. 十進制浮點數算法
- 12. 接下來?
- 13. 交互式輸入行編輯歷史回溯
- 13.1. 行編輯
- 13.2. 歷史回溯
- 13.3. 快捷鍵綁定
- 13.4. 其它交互式解釋器
- 14. 浮點數算法:爭議和限制
- 14.1. 表達錯誤
- 15. 附錄
- 15.1. 交互模式