揭秘Python中的args与kwargs——简译与总结

最近看到一篇详细介绍Python中args与kwargs关键词的文章,自己对与这两个关键词的用法很不是很熟练,在此搬运分享一下:

原文地址:Python args and kwargs: Demystified

这个教程在细节上非常详尽,很适合初学者阅读。

Python程序的函数定义中经常出现两个奇怪的参数:args与kwargs。这篇教程将对两者的用法进行详细的揭秘,教导你如何更灵活地使用args与*kwargs创建自己的函数。

通过本文你将学习到:

  • *args与**kwargs的实际意义;
  • 如何在函数定义中使用args与*kwargs
  • 如何使用*解包迭代器
  • 如何使用**解包字典

向函数传递多个参数

*args与**kwargs允许你向函数传递多个参数或者关键字参数。下面的代码是一个接收两个参数并返回它们的和的函数:

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

这个函数能够完美运行,但是受限于进能够接收两个参数。如果你需要对一组未知数量的数求和,难道要每次根据不同的数量来建立不同的函数吗?

在函数定义中使用args变量

对于上面的疑问,我们的回答当然是"No!"。Python提供了多种像函数传递未定数量参数的方法。人们最常用的便是将一个包含所有参数的列表或者集合传向函数。此时我们的my_sum函数将变为下面的形式:

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

这个方法的确可以解决问题,但你仍然需要在调用函数前建立一个列表。这将工作变得繁琐,尤其在你不知道列表中应该包含那些内容的时候。

这将是*args发挥作用的地方,它能够允许你传递不同数量的位置参数,请看下面的例子:

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

在这个新的例子中,你不再需要向函数传递一个列表。取而代之的是三个不同的位置参数,my_sum函数将这些参数打包入一个名为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))

所以说真正重要的是这里的解包操作符*。*这里传入的其实是一个元组而非列表,元组与列表都支持迭代和切片,但元组是不可变的。我们可以用下列代码测试一下两者的区别:

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

运行以后你会发现列表中的首个元素变成了9:

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

而元组则不然,强行改变其值会导致错误:

# 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

在函数定义中使用kwargs变量

现在你了解了*args的用法,那**kwargs又是怎样的呢?两者其实即为相似,但是**kwargs传递的并非位置参数而是关键字参数。示例如下:

# 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="!"))

运行上面的脚本时,concatenate()将遍历kwargs传入的字典并汇聚其中所有的值:

$ python concatenate.py
RealPythonIsGreat!

同args一样,kwargs也仅仅是一个名称,其关键是解包操作符**。上文中的例子同样可以改写为:

# 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="!"))

注意上述例子中遍历的是一个标准字典,如果你需要像实例中一样遍历一个字典的值,不要忘了使用.values()。如果你忘记了使用该方法,你就会发现遍历的结果变成了字典的键:

# 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

函数参数的顺序

现在你基本了解了args和kwargs的用法,你开始准备运用新学到的知识编写一个复杂函数,但是我们该如何将这些位置参数和命名变量放置在一起呢?

此时你就该注意函数中参数的顺序了,如同不具有默认值的参数应该放在具有默认值参数之前一样,*args应当放置在**kwargs之前才能保证函数运转正常。

简单来说,函数中的参数顺序应当为:

  1. 标准参数
  2. *args参数
  3. **kwargs参数

正确的示例:

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

如果我们把顺序写错了会怎样呢?亲自试错一下也许跟有助于记忆:

# 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

*操作符的解包操作

现在我们已经初步了解了*与**操作符在函数参数中的作用,接下来我们继续深入学习*与**的功能。

操作符*与**在Python2中首次使用,并在PEP 448的帮助下在Python3.5中变得更加强大。简而言之,解包操作符是在Python中从可迭代对象中解包值的操作符。单星号运算符*可以用于Python提供的任何迭代器,而双星号运算符**只能用于字典。

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

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

若在print()函数中对my_list用*进行解包,则变为:

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

$ python print_unpacked_list.py
1 2 3

此时print()打印的不再是列表,而是列表中具体的值。在自己定义的函数中同样可以使用*操作符,效果同样:

# 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

上述用法中需要保证函数的参数个数于解包对象的值个数是一致的,否则会导致语法错误。

当使用*操作符解包列表并将参数传递给函数时,就好像单独传递每个参数一样。这意味着可以使用多个解包操作符从多个列表中获取值,并将它们全部传递给一个函数。

# 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

*操作符还有更为方便的用法,比如说,你需要把一个数组分成三部分:首值,末值以及所有中间值。可以通过如下的方式实现:

# 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

你甚至可以用*操作符实现拆分后再合并的功能:

# 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]

同样的操作通过**操作符也可以在字典间实现:

# 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}

这里如果使用*操作符的话将仅对字典的键进行解包。

*操作符还可以对字符串进行解包:

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

$ python string_to_list.py
['R', 'e', 'a', 'l', 'P', 'y', 't', 'h', 'o', 'n']
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容