1. 程式人生 > >*args和**kwargs總結

*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)


轉載自 : https://www.jianshu.com/p/61507f60fa29