揭秘Python中的args與kwargs——簡譯與總結(jié)

最近看到一篇詳細(xì)介紹Python中args與kwargs關(guān)鍵詞的文章,自己對與這兩個關(guān)鍵詞的用法很不是很熟練,在此搬運(yùn)分享一下:

原文地址:Python args and kwargs: Demystified

這個教程在細(xì)節(jié)上非常詳盡,很適合初學(xué)者閱讀。

Python程序的函數(shù)定義中經(jīng)常出現(xiàn)兩個奇怪的參數(shù):args與kwargs。這篇教程將對兩者的用法進(jìn)行詳細(xì)的揭秘,教導(dǎo)你如何更靈活地使用args與*kwargs創(chuàng)建自己的函數(shù)。

通過本文你將學(xué)習(xí)到:

  • *args與**kwargs的實際意義;
  • 如何在函數(shù)定義中使用args與*kwargs
  • 如何使用*解包迭代器
  • 如何使用**解包字典

向函數(shù)傳遞多個參數(shù)

*args與**kwargs允許你向函數(shù)傳遞多個參數(shù)或者關(guān)鍵字參數(shù)。下面的代碼是一個接收兩個參數(shù)并返回它們的和的函數(shù):

def my_sum(a,b):
    return a + b

這個函數(shù)能夠完美運(yùn)行,但是受限于進(jìn)能夠接收兩個參數(shù)。如果你需要對一組未知數(shù)量的數(shù)求和,難道要每次根據(jù)不同的數(shù)量來建立不同的函數(shù)嗎?

在函數(shù)定義中使用args變量

對于上面的疑問,我們的回答當(dāng)然是"No!"。Python提供了多種像函數(shù)傳遞未定數(shù)量參數(shù)的方法。人們最常用的便是將一個包含所有參數(shù)的列表或者集合傳向函數(shù)。此時我們的my_sum函數(shù)將變?yōu)橄旅娴男问剑?/p>

# sum_integers_list.py
def my_sum(my_integers):
    result = 0
    for x in my_integers:
        result += x
    return result

list_of_integers = [1,2,3]
print(mu_sum(list_of_integers))

這個方法的確可以解決問題,但你仍然需要在調(diào)用函數(shù)前建立一個列表。這將工作變得繁瑣,尤其在你不知道列表中應(yīng)該包含那些內(nèi)容的時候。

這將是*args發(fā)揮作用的地方,它能夠允許你傳遞不同數(shù)量的位置參數(shù),請看下面的例子:

# sum_integers_args.py
def my_sum(*args):
    result = 0
    # Iterating over the Python args tuple
    for x in args:
        result += x
    return result

print(my_sum(1,2,3))

在這個新的例子中,你不再需要向函數(shù)傳遞一個列表。取而代之的是三個不同的位置參數(shù),my_sum函數(shù)將這些參數(shù)打包入一個名為args的單一迭代對象。

注意args其實只是一個名稱,你不必非得使用這幾個字母,可以用任意其他名稱來替代:

# sum_integers_args_2.py
def my_sum(*integers):
    result = 0
    for x in integers:
        result += x
    return result

print(my_sum(1,2,3))

所以說真正重要的是這里的解包操作符*。*這里傳入的其實是一個元組而非列表,元組與列表都支持迭代和切片,但元組是不可變的。我們可以用下列代碼測試一下兩者的區(qū)別:

# change_list.py
my_list = [1,2,3]
my_list[0] = 9
print(my_list)

運(yùn)行以后你會發(fā)現(xiàn)列表中的首個元素變成了9:

$ python change_list.py
[9,2,3]

而元組則不然,強(qiáng)行改變其值會導(dǎo)致錯誤:

# change_tuple.py
my_tuple = (1,2,3)
my_tuple[0] = 9
print(my_tuple)

$ python change_tuple.py
Traceback (most recent call last):
    File "change_tuple.py", line 3, in <module>
        my_tuple[0] = 9
TypeError: 'tuple' object does not support item assignment

在函數(shù)定義中使用kwargs變量

現(xiàn)在你了解了*args的用法,那**kwargs又是怎樣的呢?兩者其實即為相似,但是**kwargs傳遞的并非位置參數(shù)而是關(guān)鍵字參數(shù)。示例如下:

# concatenate.py
def concatenate(**kwargs):
    result = ""
    # Iterating over the Python kwargs dictionary
    for arg in kwargs.values():
        result += arg
    return result

print(concatenate(a="Real",b="Python",c="Is",d="Great",e="!"))

運(yùn)行上面的腳本時,concatenate()將遍歷kwargs傳入的字典并匯聚其中所有的值:

$ python concatenate.py
RealPythonIsGreat!

同args一樣,kwargs也僅僅是一個名稱,其關(guān)鍵是解包操作符**。上文中的例子同樣可以改寫為:

# concatenate_2.py
def concatenate(**words):
    result = ""
    for arg in words.values():
        result += arg
    return result

print(concatenate(a="Real",b="Python",c="Is",d="Great",e="!"))

注意上述例子中遍歷的是一個標(biāo)準(zhǔn)字典,如果你需要像實例中一樣遍歷一個字典的值,不要忘了使用.values()。如果你忘記了使用該方法,你就會發(fā)現(xiàn)遍歷的結(jié)果變成了字典的鍵:

# concatenate_keys.py
def concatenate(**kwargs):
    result = ""
    # Iterating over the keys of the Python kwargs dictionary
    for arg in kwargs:
        result += arg
    return result

print(concatenate(a="Real",b="Python",c="Is",d="Great",e="!"))

$ python concatenate_keys.py
abcde

函數(shù)參數(shù)的順序

現(xiàn)在你基本了解了args和kwargs的用法,你開始準(zhǔn)備運(yùn)用新學(xué)到的知識編寫一個復(fù)雜函數(shù),但是我們該如何將這些位置參數(shù)和命名變量放置在一起呢?

此時你就該注意函數(shù)中參數(shù)的順序了,如同不具有默認(rèn)值的參數(shù)應(yīng)該放在具有默認(rèn)值參數(shù)之前一樣,*args應(yīng)當(dāng)放置在**kwargs之前才能保證函數(shù)運(yùn)轉(zhuǎn)正常。

簡單來說,函數(shù)中的參數(shù)順序應(yīng)當(dāng)為:

  1. 標(biāo)準(zhǔn)參數(shù)
  2. *args參數(shù)
  3. **kwargs參數(shù)

正確的示例:

# correct_function_definition.py
def my_function(a,b,*args,**kwargs):
    pass

如果我們把順序?qū)戝e了會怎樣呢?親自試錯一下也許跟有助于記憶:

# wrong_function_definition.py
def my_function(a,b,**kwargs,*args):
    pass

$ python wrong_function_definition.py
File "wrong_function_definition.py", line 2
    def my_function(a,b,**kwargs,*args):
                                 ^
SyntaxError: invalid syntax

*操作符的解包操作

現(xiàn)在我們已經(jīng)初步了解了*與**操作符在函數(shù)參數(shù)中的作用,接下來我們繼續(xù)深入學(xué)習(xí)*與**的功能。

操作符*與**在Python2中首次使用,并在PEP 448的幫助下在Python3.5中變得更加強(qiáng)大。簡而言之,解包操作符是在Python中從可迭代對象中解包值的操作符。單星號運(yùn)算符*可以用于Python提供的任何迭代器,而雙星號運(yùn)算符**只能用于字典。

# print_list.py
my_list = [1,2,3]
print(my_list)

$ python print_list.py
[1,2,3]

若在print()函數(shù)中對my_list用*進(jìn)行解包,則變?yōu)椋?/p>

# print_unpacked_list.py
my_list = [1,2,3]
print(*my_list)

$ python print_unpacked_list.py
1 2 3

此時print()打印的不再是列表,而是列表中具體的值。在自己定義的函數(shù)中同樣可以使用*操作符,效果同樣:

# unpacking_call.py
def my_sum(a,b,c):
    print(a + b + c)

my_list = [1,2,3]
my_sum(*my_list)

$ python unpacking_call.py
6

上述用法中需要保證函數(shù)的參數(shù)個數(shù)于解包對象的值個數(shù)是一致的,否則會導(dǎo)致語法錯誤。

當(dāng)使用*操作符解包列表并將參數(shù)傳遞給函數(shù)時,就好像單獨(dú)傳遞每個參數(shù)一樣。這意味著可以使用多個解包操作符從多個列表中獲取值,并將它們?nèi)總鬟f給一個函數(shù)。

# sum integers_args_3.py
def my_sum(*args):
    result = 0
    for x in args:
        result += x
    return result

list1 = [1,2,3]
list2 = [4,5]
list3 = [6,7,8,9]

print(my_sum(*list1,*list2,*list3))

$ python sum_integers_args_3.py
45

*操作符還有更為方便的用法,比如說,你需要把一個數(shù)組分成三部分:首值,末值以及所有中間值。可以通過如下的方式實現(xiàn):

# extract_list_body.py
my_list = [1,2,3,4,5,6]
a,*b,c = my_list
print(a)
print(b)
print(c)

$ python extract_list_body.py
1
[2,3,4,5]
6

你甚至可以用*操作符實現(xiàn)拆分后再合并的功能:

# merging_lists.py
my_first_list = [1,2,3]
my_second_list = [4,5,6]
my_merged_list = [*my_first_list,*my_second_list]
print(my_merged_list)

$ python merging_lists.py
[1,2,3,4,5,6]

同樣的操作通過**操作符也可以在字典間實現(xiàn):

# merging_dict.py
my_first_dict = {"A":1,"B":2}
my_second_dict = {"C":3,"D":4}
my_merged_dict = {**my_first_dict,**my_second_dict}
print(my_merged_dict)

$ python merging_dict.py
{'A':1,'B':2,'C':3,'D':4}

這里如果使用*操作符的話將僅對字典的鍵進(jìn)行解包。

*操作符還可以對字符串進(jìn)行解包:

# string_to_list.py
a = [*'RealPython']
print(a)

$ python string_to_list.py
['R', 'e', 'a', 'l', 'P', 'y', 't', 'h', 'o', 'n']
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容