*args和**kwargs總結
基本概念
Python支援可變引數,最簡單的方法莫過於使用預設引數。
def test_defargs(one, two=2): # 引數one沒有預設值,two的預設值為2
print('Required argument: ', one)
print('Optional argument: ', two)
test_defargs(1)
'''
Required argument: 1
Optional argument: 2
'''
test_defargs(1, 3)
'''
Required argument: 1
Optional argument: 3
'''
另外一種達到可變引數 (Variable Argument) 的方法:
使用 *args
和 **kwargs
語法。
*args
是可變的位置引數(positional arguments)列表,
**kwargs
是可變的關鍵詞引數(keyword arguments)列表。
並且規定位置引數必須位於關鍵詞引數之前,即 *args
必須位於 **kwargs
之前。
位置引數
def print_hello(name, sex):
sex_dict = {1: '先生', 2: '女士'} # key: value
print('hello %s %s, welcome to Python world!' %(name, sex_dict.get(sex, '先生'))) # if no such a key, print '先生'
print_hello('Chen', 2) # 位置引數要求先後順序,對應name和sex
print_hello('Chen', 3) # 兩個引數的順序必須一一對應,且少一個引數都不可以
'''
hello Chen 女士, welcome to Python world!
hello Chen 先生, welcome to Python world!
'''
關鍵字引數
用於函式呼叫,通過“鍵-值”形式加以指定。
使用關鍵詞引數可以讓函式更加清晰、容易使用,同時也清除了引數的順序需求。
以下是用關鍵字引數正確呼叫函式的例項
print_hello('Chen', sex=1) # 有位置引數時,位置引數必須在關鍵字引數的前面
print_hello(name='Chen', sex=1) # 關鍵字引數之間不存在先後順序的
print_hello(sex=1, name='Chen')
以下是錯誤的呼叫方式:
print_hello(name='Chen', 1) # 有位置引數時,位置引數必須在關鍵字引數的前面
print_hello(sex=1, 'Chen')
預設引數
使用關鍵詞引數時,可為引數提供預設值,呼叫函式時可傳可不傳該預設引數的值(注意:所有位置引數必須出現在預設引數前,包括函式定義和呼叫)
正確的預設引數定義方式 –> 位置引數在前,預設引數在後:
def print_hello2(name, sex=1): # 預設sex=1
sex_dict = {1: '先生', 2: '女士'} # key: value
print('hello %s %s, welcome to circus!' %(name, sex_dict.get(sex, '先生')))
# 錯誤的定義方式
# def print_hello2(sex=1, name):
# 呼叫時不傳sex的值,則使用預設值1
print_hello2('Chen')
'''
hello Chen 先生, welcome to circus!
'''
# 呼叫時傳入sex的值,並指定為2。無視原來的sex=1,僅本次生效,不會改變函式sex的預設值
print_hello2('Liu', sex=2)
'''
hello Liu 女士, welcome to circus!
'''
收集引數(Packing)
被收集到一起的位置引數或關鍵詞引數
有些時候,我們在定義引數時不確定會傳遞多少個引數(或者不傳遞),甚至我們想要根據實際情況傳入任意個引數,這個時候 packing
就派上了用場。
def print_args(*args):
print(args)
print_args(1)
print_args(1, 2, 3)
'''
(1,)
(1, 2, 3)
'''
可以看到,結果是作為一個元組(tuple)打印出來的,因為僅傳入1時,輸出的括號裡面有一個逗號。實際上,*args
前的 *
可以理解為將我們需要傳入的所有值放置在了同一個元組裡面,這就是收集 (packing) 的過程。
位置引數的收集傳遞 ———— *packing
def print_args2(num, *args):
print(num)
print(args)
print_args2(1)
print_args2(1, 2, 3)
'''
1 print(num)
() print(args) --> 一個空元組
1 print(num)
(2, 3) print(args) --> 剩餘的引數組成的元組!
'''
那麼這麼說來 *
的意思就是“把剩餘的所有引數收集 packing 起來”!
關鍵詞引數的收集傳遞 ———— **packing
def print_kargs(**kargs):
print(kargs)
print_kargs(x=1, y=2, z=3)
'''
{'z': 3, 'y': 2, 'x': 1}
'''
結果輸出了一個字典(dic),雙星號 **
收集了所有關鍵詞引數。
分割引數
*
和 **
也可以在函式呼叫過程中使用,稱之為分割 (unpacking)。
在傳遞元組時,讓元組的每一個元素對應一個位置引數
def print_hello(name, sex):
print(name, sex)
args = ('Chen', '男')
print_hello(*args)
'''
Chen 男
'''
在傳遞詞典字典時,讓詞典的每個鍵值對作為一個關鍵字引數傳遞給函式
def print_info(**kargs):
print(kargs)
kargs = {'name': 'Chen', 'sex': '男', 'time': '13'}
print_info(**kargs)
'''
{'name': 'Chen', 'sex':'男','time':'13'}
'''
位置引數、關鍵詞引數(預設引數)、收集引數的混合使用
基本原則是:先位置引數,預設引數,收集位置引數,收集關鍵字引數(定義和呼叫都應遵循)
def func(name, age, sex=1, *args, **kargs):
print(name, age, sex, args, kargs)
func('Chen', 25, 2, 'Geo', 'SR', level=2)
'''
Chen 25 2 ('Geo', 'SR') {'level': 2}
'''
總結一下就是,星號 *
只有在定義需要使用不定數目的引數的函式,或者呼叫“分割”字典和序列時才有必要新增。
各類引數混合使用
EXAMPLE 1
Tell a story
def story1(**kwargs):
return 'Once upon a time, there was a %(job)s called %(name)s.' % kwargs
def story2(**kwargs):
return 'In the year of our lord %(year)d, there once lived a %(job)s of a royal line.' % kwargs
print(story1(name='Robin', job='brave knight'))
print(story1(job='king', name='Charlie'))
'''
Once upon a time, there was a brave knight called Robin.
Once upon a time, there was a king called Charlie.
'''
kwargs = {'name': 'Abel', 'job': 'bard'}
print(story1(**kwargs))
'''
Once upon a time, there was a bard called Abel.
'''
kwargs.pop('job') # delete key['job'] --> kwargs = {'name': 'Abel',}
print(story1(job='witch', **kwargs)) # redefine key['job']
'''
Once upon a time, there was a witch called Abel.
'''
print(story2(year=1239, job='prince'))
print(story2(job='princess', year=1239))
'''
In the year of our lord 1239, there once lived a prince of a royal line.
In the year of our lord 1239, there once lived a princess of a royal line.
'''
EXAMPLE 2
x to the yth power
def power(x, y, *args):
if args:
print('Received redundant parameters:', args)
return pow(x, y)
print(power(2, 3)) # 2 to the 3rd power
print(power(3, 2)) # 3 to the 2nd power
print(power(x=2, y=3)) # 2 to the 3rd power
print(power(y=3, x=2)) # no order for keyword arguments
'''
8
9
8
8
'''
args = (5,) * 2 # '(5,) * 2': copy elements in the tuple 2 times. The outcome is (5, 5)
print(power(*args)) # 5 to the 5th power
'''
3125
'''
print(power(2, 5, 'Keep on trying'))
'''
Received redundant parameters: ('Keep on trying',)
32 # 2 to the 5th power
'''
EXAMPLE 3
Bulid a function to realize range() for step>0
def interval(start=0, stop=None, step=1):
if stop is None:
start, stop = 0, start
result = []
i = start
while i < stop:
result.append(i)
i += step
return result
print(interval(10))
'''
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
'''
print(interval(1, 6))
'''
[1, 2, 3, 4, 5]
'''
print(interval(3, 15, 3))
[3, 6, 9, 12]
POSTSCRIPT
在Python 3.X當中加入了”部分剩餘引數”的概念。舉例如下 :
a,\*b,c = range(5)
中間的 *b 實際上是收集到了3個引數。
下面這個例子更加直觀 :
def func1(x, y, *args, z=1): # z=1 在函式引數中最後定義
print(x, y, args, z)
func1(1, 2, 3, 4, 5)
'''
1 2 (3, 4, 5) 1
'''
Python 3.X 當中,預設引數 z=1 在函式引數中最後定義,Python 就會知道除了 傳給 x 和 y, 以及 z=1 的部分,其他的剩餘引數 (3, 4, 5) 都是傳入到 *args 當中。
但在Python 2.X 並沒有這一特性,所以 Python 2.X 的 z=1 必須在args和*kwargs這種剩餘引數收集之前。
預設引數 z=1 不是在函式引數中最後定義時,情況又是怎樣?
def func2(x, y, z=1, *args):
print(x, y, z, args)
func2(1, 2, 3, 4, 5)
'''
1 2 3 (4, 5)
'''
3傳入到 z ,而 args=(4, 5)