深入解析Python函数传递不定个数参数的机制与应用
在上一篇文章《[Python]向函数传递不定数量的参数》中,我们主要讲解了*在函数定义和函数调用阶段的不同作用,并留下了一个小问题:

我们用*定义了add_int(*a)函数,现在a_list=[1,2,3],那么调用add_int(*a_list)会产生什么效果呢?
如果你看过文章,不难知道最后传递给函数的a是(1,2,3),因为它已经用*先解压了——将a_list改为1,2,3并传递了它to 函数中,即add_int(1, 2, 3),然后用 * 包装——将传入的 1, 2, 3 变成 (1, 2, 3) 并赋值给形参 a函数
同时我们留下了上一篇文章中没有讲清楚的一点——python函数中传递参数的两种方式。这里我们将详细展开,介绍一个类似*的新符号的用法
参数传递的两种方式:位置传递和关键字传递
假设您已经预定义了一个将两个数字相加的函数:
def add_2_int(a1, a2):
return a1+a2
现在要计算1+2的结果,最自然的调用方法是add_2_int(1,2),这样在函数中将1赋值给a1,将2赋值给a2。这种分配方法是基于1、2和a1、a2。值是根据一一位置对应来赋值的,所以称为位置转移。事实上,我们还可以有另一种调用方法:add_2_int(a2=2, a1=1),它显式地告诉函数我要给a2赋值2,给a1赋值1。这是根据参数关键字给参数赋值。它与顺序或位置无关,因此称为按关键字传递。
基于此,我们再回顾一下上一篇文章中*的用法。如果我们的调用方法是add_2_int(*[1,2]),那么这是什么样的参数传递方法呢? ——答案当然是按位置传递,因为[1,2]被*解压成1,2传递给函数,相当于add_2_int(1,2)。
说到这里,聪明的读者一定会问,通过关键字传递是否也有类似的“解压”用法呢? - 瞧不起
让我们从一个例子开始
我们先定义一个简单的类——Human,定义如下
class human():
def __init__(self, gender = 'female', height = 160, weight = 55):
print 'gender: ', gender
print 'height: ', height
print 'weight: ', weight
共有三个基本属性:性别、身高、体重。这三个基本属性都有默认值。
诚实
接下来我们实例化一个人并将其命名为 Catherine
Catherine = human(height=173,weight='67')
凯瑟琳的性别遵循人类类别的默认值,因此在参数中没有给出。身高和体重是根据Catherine的实际情况调整的,所以实例化该类后的结果是
gender: female
height: 173
weight: 67
不难发现,我们这里初始化类时,采用的方法是通过关键字来传递,而关键字及其值很容易让人联想到字典中的键和值,所以——如果关键字及其值该值是“字典”的形式,那么我们可以使用“解压”的一些方法,然后传递给函数,而对于“字典”的解压方法,显然不能使用上面提到的*,因为*只能解压缩序列,例如列表、元组等,这里使用的解压方法是* - **的孪生兄弟。具体用法如下:
dict_Catherine = {'weight':67,'height':173}
Catherine = human(**dict_Catherine)
**dict_Catherine中的key:value对会被自动解压,并将value赋给函数中与key同名的形参(注意这是在函数调用阶段使用的**)
突变体
然而,只有三种基本属性的人类显然是弱小的。这个世界上依然存在着强大的变种人。为了迎合变种人各种飘忽不定的属性,我们必须重新定义一个新的 human_mutant 类来适应这个新人类“不确定”的属性要求,如下:
class human_mutant():
def __init__(self, gender = 'male', height = 170, weight = 65, **X):
print 'gender: ', gender
print 'height: ', height
print 'weight: ', weight
for key,value in X.iteritems():
print key, ': ',value
不难发现,除了人类的三大属性之外,变种人还拥有专门为他们量身定制的未知X元素,并利用**作用于X元素。效果如何?我们看一下实例化的结果。
这里我们首先实例化X教授,代码如下
ProfessorX = human_mutant(ability = 'mind-control', hairstyle = 'bald', height = 175)
我们为X教授添加了能力、发型等新参数,并修改了身高。这些新的参数在函数定义的时候并没有给出,显然是写入到X中了。运行效果是
gender: male
height: 175
weight: 65
hairstyle : bald
ability : mind-control
因此,**在函数定义阶段的作用就是将函数参数中没有出现的关键字传递的参数及其值收集并“打包”到字典中。比如我们初始化Professor Mind-control时,'hairstyle': 'bald'},赋值给参数X。
好的,差不多就这样了。让我们玩得开心并实例化一个我非常喜欢的万磁王。
Magneto = human_mutant(ability = 'metal-control', guard = 'helmet', height = 180, lover = 'ProfessorX')
我们给了他新的防护装备——头盔,超能力——控制金属,还有他的好朋友——X教授。
总结
至此,我们已经弄清楚如何将不定“数字”和不定“关键字”传递给Python中的函数。以下是一个简短的总结:
当函数被定义时,
*可以将按位置传入的参数“打包”成元组类型
**可以将关键字传入的参数“封装”成字典类型
当函数被调用时,
*可以对要传递给函数的元组、列表、集合、字符串等类型进行“解压”(文中我没有提到字符串的解压,有兴趣的读者可以自己尝试一下),然后将它们传递给函数按位置。函数入口参数
**可以将要传递给函数的字典“解压”,并通过关键字传递给函数入口参数