Python学习笔记(10)——函数参数

本章继续学习函数的知识。

本文仅供个人记录和复习,不用于其他用途

默认参数

比如我们现在定义一个函数,用于计算x²:

1
2
def power(x):
return x * x

那么如果我现在想计算x的次方,是不是又得重新写一遍呢?为了函数的泛用性,我们可以为它添加一个参数y,用于表示函数的指数:

1
2
3
4
5
6
def power(x, n):
sum = 1
while n > 0:
n -= 1
sum *= x
return sum

由于我们经常要计算x的平方,所以我们可以给n设定一个默认值:

1
2
3
4
5
6
def power(x, n = 2):
sum = 1
while n > 0:
n -= 1
sum *= x
return sum

如果不设定默认值,那么我们每次都得传入x的指数。而这个默认值能够让我们不传入指数,让Python默认执行x的平方运算。

注意一下,必选参数在前,默认参数在后,这个顺序是不能够改变的。想一想,如果默认参数可以在必选参数前,那么我们如何修改默认参数呢?如果我们每次都要提供默认参数,那么它显然就失去了默认的意义。

如果我们在调用函数时,需要修改默认参数的值,那么可以按照顺序提供参数。不过有的时候你想修改第二个默认参数,那么怎么办呢?

1
2
3
4
5
def dataIn(name, age, city = 'beijing', province = 'beijing'):
print name
print age
print city
print province

上面是一个拥有两个默认参数的函数,如果你只想改变province的值,那么可以调用dataIn(‘miao’, 18, province = ‘tianjing’)

默认参数的陷阱

比如我们定义一个函数,用于给list添加一个结束符‘End’

1
2
3
4
5
6
def addEnd(L = []):
L.append('End')
return L
print addEnd([1, 2, 3])
print addEnd(['x', 'y', 'z'])

你会发现效果还可以:

[1, 2, 3, 'End']
['x', 'y', 'z', 'End']

那么我们这么做呢:

1
print addEnd()

结果看起来是对的:

['End']

但是当我们添加多个时:

1
2
3
print addEnd()
print addEnd()
print addEnd()

就会得到下面的结果:

['End']
['End', 'End']
['End', 'End', 'End']

为什么会这样呢?我们设置的默认参数是[],而且应该是一个临时变量,函数结束时自动删除,怎么会记录之前的数据呢?

事实上,Python的函数有些不同,默认参数在函数定义时就已经创建。如果默认参数是一个可变对象,而且函数中也改变了它的值。那么Python就会保留这个可变对象,并且作为下次调用该函数时的默认值。

由于这个不同的之处,我们每添加一次‘End’,默认参数[]都会将它记录下来。因此,默认参数必须指向不变对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;
string addEnd(string str = "")
{
str.append("End");
return str;
}
int main()
{
cout << addEnd();
cout << addEnd();
}

C++就不会产生这种情况,默认参数不会记录之前的值。那么怎么解决这个问题呢?

1
2
3
4
5
def addEnd(L = None):
if L is None:
L = []
L.append('End')
return L

由于默认参数是None,是一个不可变对象。因此Python不会保留它的值,哪怕它在函数体中被修改过。所以,我们能使用不可变对象时,最好不要使用类似于list的可变对象。

可变参数

我们还可以定义可变参数,也就是传入参数的个数可变。比如我们要计算a² + b² + c² + …,那么这个时候参数不确定,我们只能够将a、b、c…作为一个tuple或list传入:

1
2
3
4
5
def calc(num):
sum = 0
for n in num:
sum = sum + n * n
return sum

那么调用这个函数时,就必须用list或tuple包装:

1
print calc([1, 2, 3])

这样调用函数显得有些麻烦,我们可以将参数变为可变参数:

1
2
3
4
5
def calc(*num):
sum = 0
for n in num:
sum = sum + n * n
return sum

我们只需要添加一个*,就可以传入任意个参数,包括0个参数:

1
2
print calc(1, 2 ,3)
print calc()

但是呢,假如我还是要传入一个list或者tuple,那么怎么办呢?如果直接传入是不行的,因为可变参数不能被list初始化。你当然可以这么做:

1
2
nums = [1, 2, 3]
print calc(nums[0], nums[1], nums[2])

不过这样太麻烦了,我们可以在num前面加上一个*,这样就可以把list中的元素转变为可变参数传入:

1
print calc(*nums)

关键字参数

可变参数可以接受任意个数的参数,并且把它们包装成tuple,给函数块调用。而关键字参数则是接受任意个含参数名的参数,并且包装成一个dict:

1
2
def person(name, age, **kw):
print 'name:', name, 'age:', age, 'other:', kw

nameage是必选参数,而kw是关键字参数。我们可以只调用必选参数:

1
print person('miao', 18)

同时也可以传入任意个数的关键字参数:

1
print person('miao', 18, city = 'shanghai', job = 'student')

输出如下:

name: miao age: 18 other: {'city': 'shanghai', 'job': 'student'}

关键字参数可用于扩展函数功能,比如用户注册时,有可选项和必选项,那么这就可以用到关键字参数。当然,你也可以直接将dict传入,不过这样很复杂,没有关键字参数方便。

多种参数组合

比如定义一个函数,包含以上四种参数形式:

1
2
def func(a, b, c=0, *args, **kw):
print 'a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw

事实上,这个函数用起来不是很方便。如果你要输入可变参数或者关键字参数,那么你就必须要提供默认参数。比如:

1
func(1, 2, 3, 'a', 'b', c = 99)

你以为是正确的?其实是错的。因为c其实是默认参数,所以你不能够设置c为关键字:

1
func(1, 2, 3, 'a', 'b', d = 99)

结果输出如下:

a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'d': 99}

总之,我们在使用参数列表时,一定要注意参数的顺序。