翻譯:《實用的Python編程》07_01_Variable_arguments
目錄 | 上一節 (6.4 生成器表達式) | 下一節 (7.2 匿名函數)
7.1 可變參數
本節介紹可變(variadic)參數。有時,可變參數使用 *args 和 **kwargs 進行表示。
可變位置參數(*args)
如果一個函數接受任意數量的(位置)參數,那么我們稱該函數使用了可變參數(variable arguments)。示例:
def f(x, *args):
...
函數調用:
f(1,2,3,4,5)
額外的參數作為元組進行傳遞:
def f(x, *args):
# x -> 1
# args -> (2,3,4,5)
可變關鍵字參數(**kwargs)
一個函數也可以接受任意數量的關鍵字參數。示例:
def f(x, y, **kwargs):
...
函數調用:
f(2, 3, flag=True, mode='fast', header='debug')
額外的參數作為字典進行傳遞:
def f(x, y, **kwargs):
# x -> 2
# y -> 3
# kwargs -> { 'flag': True, 'mode': 'fast', 'header': 'debug' }
可變位置參數與可變關鍵字參數結合使用
一個函數可以同時接受可變非關鍵字參數和可變關鍵字參數。
def f(*args, **kwargs):
...
函數調用:
f(2, 3, flag=True, mode='fast', header='debug')
這些參數被分為位置參數和關鍵字參數兩部分。
def f(*args, **kwargs):
# args = (2, 3)
# kwargs -> { 'flag': True, 'mode': 'fast', 'header': 'debug' }
...
上述函數接受任意數量的位置參數和關鍵字參數。當編寫包裝器(wrappers)或要將參數傳遞給另一個函數時使用。
傳遞元組和字典
元組可擴展為可變參數:
numbers = (2,3,4)
f(1, *numbers) # Same as f(1,2,3,4)
字典也可以擴展為關鍵字參數:
options = {
'color' : 'red',
'delimiter' : ',',
'width' : 400
}
f(data, **options)
# Same as f(data, color='red', delimiter=',', width=400)
練習
練習 7.1: 可變參數的簡單示例
嘗試定義下列函數:
>>> def avg(x,*more):
return float(x+sum(more))/(1+len(more))
>>> avg(10,11)
10.5
>>> avg(3,4,5)
4.0
>>> avg(1,2,3,4,5,6)
3.5
>>>
請注意 *more 是如何收集其它所有參數的。
練習 7.2:將元組和字典作為參數進行傳遞
假設你從文件中讀取數據,并獲得一個元組。例如:
>>> data = ('GOOG', 100, 490.1)
>>>
現在,假設你想根據上面的數據創建一個 Stock 對象。如果你直接傳 data ,那就行不通了:
>>> from stock import Stock
>>> s = Stock(data)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() takes exactly 4 arguments (2 given)
>>>
這個問題很容易解決,直接使用 *data 即可。試試看:
>>> s = Stock(*data)
>>> s
Stock('GOOG', 100, 490.1)
>>>
如果你擁有的是一個字典,那么你可以改用 **。示例:
>>> data = { 'name': 'GOOG', 'shares': 100, 'price': 490.1 }
>>> s = Stock(**data)
Stock('GOOG', 100, 490.1)
>>>
練習 7.3:創建實例列表
在 report.py 程序中,我們使用如下代碼創建了一個實例列表:
def read_portfolio(filename):
'''
Read a stock portfolio file into a list of dictionaries with keys
name, shares, and price.
'''
with open(filename) as lines:
portdicts = fileparse.parse_csv(lines,
select=['name','shares','price'],
types=[str,int,float])
portfolio = [ Stock(d['name'], d['shares'], d['price'])
for d in portdicts ]
return Portfolio(portfolio)
我們可以改用 Stock(**d) 來簡化代碼。請完成修改。
練習 7.4:參數傳遞
fileparse.parse_csv() 函數具有一些選項,用于更改文件分隔符和錯誤報告。也許你會想把這些選擇暴露給上面的 read_portfolio() 函數。請完成修改:
def read_portfolio(filename, **opts):
'''
Read a stock portfolio file into a list of dictionaries with keys
name, shares, and price.
'''
with open(filename) as lines:
portdicts = fileparse.parse_csv(lines,
select=['name','shares','price'],
types=[str,int,float],
**opts)
portfolio = [ Stock(**d) for d in portdicts ]
return Portfolio(portfolio)
修改完成后,嘗試閱讀讀取一些帶有錯誤的文件:
>>> import report
>>> port = report.read_portfolio('Data/missing.csv')
Row 4: Couldn't convert ['MSFT', '', '51.23']
Row 4: Reason invalid literal for int() with base 10: ''
Row 7: Couldn't convert ['IBM', '', '70.44']
Row 7: Reason invalid literal for int() with base 10: ''
>>>
現在,嘗試隱藏錯誤:
>>> import report
>>> port = report.read_portfolio('Data/missing.csv', silence_errors=True)
>>>
浙公網安備 33010602011771號