
5.3.2 函数的参数
在C、C++中,参数的传递有值传递和引用传递两种方式。而Python中任何东西都是对象,所以参数只支持引用传递的方式。Python通过名称绑定的机制,把实际参数的值和形式参数的名称绑定在一起。即把形式参数传递到函数所在的局部命名空间中,形式参数和实际参数指向内存中同一个存储空间。
函数的参数支持默认值。当某个参数没有传递实际的值时,函数将使用默认参数计算。下面【例5-3】这段代码可以给arithmetic()的参数都提供一个默认值。
【例5-3.py】
01 # 函数的默认参数 02 def arithmetic(x=1, y=1, operator="+"): # 定义arithmetic函数 03 result = { # 定义result字典 04 "+" : x + y, 05 "-" : x - y, 06 "*" : x * y, 07 "/" : x / y 08 } 09 return result.get(operator) # 调用get方法返回计算结果 10 11 print (arithmetic(1, 2)) 12 print (arithmetic(1, 2, "-")) 13 print (arithmetic(y=3, operator="-")) 14 print (arithmetic(x=4, operator="-")) 15 print (arithmetic(y=3, x=4, operator="-"))
【代码说明】
·第2行代码使用赋值表达式的方式定义参数的默认值。
·第11行代码,参数x、y的值分别赋值为1、2,参数operator使用默认值“+”。输出结果为“3”。
·第12行代码,提供了3个实际参数,这3个值将分别覆盖形式参数的默认值。输出结果为“-1”。
·第13行代码,指定参数y、operator的值。输出结果为“-2”。这里必须使用赋值表达式的方式传递参数;否则,Python解释器将误认为x=3、y="-"。因此下面的写法是错误的。
print (arithmetic(3, "-"))
·第14行代码,指定参数x、operator的值。输出结果为“3”。
·第15行代码,使用赋值表达式传递参数,可以颠倒参数列表的顺序。输出结果为“1”。
参数可以是变量,也可以是元组、列表等内置数据结构。下面【例5-4】这段代码实现列表作为参数传递。
【例5-4.py】
01 # 列表作为参数传递 02 def arithmetic(args=[], operator="+"): 03 x = args[0] 04 y = args[1] 05 result = { 06 "+" : x + y, 07 "-" : x - y, 08 "*" : x * y, 09 "/" : x / y 10 } 11 return result.get(operator) 12 print (arithmetic([1, 2]))
【代码说明】
·第2行代码把参数x、y合并为一个参数,通过args列表传递x、y的值。
·第3、4行代码,从列表中取出参数值分别赋值给变量x、y。
·第12行代码,把列表[1,2]传递给arithmetic()。输出结果为“3”。
由于参数实现了名称绑定的机制,在使用默认参数时,可能会出现预期之外的结果。
01 def app(args=[]): 02 args.append(0) 03 print (args) 04 05 app() 06 app([1]) 07 app()
【代码说明】
·第1行代码定义了一个app()函数,参数是一个默认的列表。
·第2行代码在列表中追加一个元素0。
·第5行代码调用app(),使用默认的列表。输出结果为“[0]”。
·第6行代码,传递了一个列表[1],app()中追加一个元素1。输出结果为“[1,0]”。
·第7行代码再次调用app(),此时使用的列表还是第一次调用的args,因此args在原有的基础上将再次追加一个元素0。输出结果为“[0,0]”。
为了避免这个问题,可以在append()中添加一个条件判断语句。如果列表args中没有任何元素,则先把args列表置空,然后再添加元素。下面通过【例5-5】的代码实现。
【例5-5.py】
01 def app1(args=[]): 02 if len(args) <= 0: 03 args = [] 04 args.append(0) 05 print (args) 06 07 app1() 08 app1([1]) 09 app1()
【代码说明】
·第2行代码使用len()判断列表args的长度是否大于0。如果小于等于0,则把args置为空列表,即取消了函数参数的绑定。
·第4行代码在列表中追加一个元素0。
·第7行代码调用app1(),使用默认的列表。输出结果为“[0]”。
·第8行代码,传递了一个列表[1],app1()中追加一个元素0。输出结果为“[1,0]”。
·第9行代码调用app1(),通过len(args)的判断,取消了参数的名字绑定。输出结果为“[0]”。
在开发中,常常需要传递可变长度的参数。在函数的参数前使用标识符“*”可以实现这个要求。“*”可以引用元组,把多个参数组合到一个元组中。
01 # 传递可变参数 02 def func1(*args): 03 print(args) 04 func1(1, 2, 3)
【代码说明】
·第2行代码,在参数args前使用标识符“*”。
·第3行代码输出参数的值,由于参数使用了“*args”的形式,因此传入的实际参数被“打包”到一个元组中,输出结果为“(1,2,3)”。
·第4行代码调用函数func1()。其中的参数“1”、“2”、“3”成为args元组的元素。
Python还提供另一个标识符“**”。在形式参数前面添加“**”,可以引用一个字典,根据实际参数的赋值表达式生成字典。例如,下面这段代码实现了在一个字典中匹配元组的元素。定义函数时,设计两个参数:一个是待匹配的元组,表示为“*t”;另一个是字典,表示为“*d”。函数调用时,实际参数分成两部分:一部分参数是若干个数字或字符串,另一部分参数是赋值表达式,如图5-4所示。下面通过【例5-6】的代码实现。

图5-4 “*”、“**”与实际参数的对应关系
【例5-6.py】
01 # 传递可变参数 02 def search(*t, **d): # 定义search函数 03 keys = d.keys() 04 values = d.values() 05 print(keys) 06 print (values) 07 for arg in t: 08 for key in keys: 09 if arg == key: 10 print ("find:",d[key]) 11 12 search("one", "three", one="1",two="2",three="3") # 调用search函数
【代码说明】
·第2行代码中的“*t”与第12行代码中的“one”、“three”对应。“one”、“three”组成一个元组t。“**d”与“one="1",two="2",three="3"”对应,生成一个字典{one:"1",two:"2",three:"3"}。
·第5行代码输出结果:
dict_keys(['one', 'two', 'three'])
·第6行代码输出结果:
dict_values(['1', '2', '3'])
·第7行到第10行代码在字典d中查找元组t中的值。如果找到,则输出。输出结果如下所示。
find: 1 find: 3
注意 “*”必须写在“**”的前面,这是语法规定。