python中的默认参数

Part 1

偶然看到一道Python面试题(链接):
请写出下面的代码的输出是什么?

1
2
3
4
5
6
7
8
9
10
11
def extendList(val, list=[]):
list.append(val)
return list
list1 = extendList(10)
list2 = extendList(123, [])
list3 = extendList('a')
print "list1 = %s" % list1
print "list2 = %s" % list2
print "list3 = %s" % list3

实际输出结果为:

1
2
3
list1 = [10, 'a']
list2 = [123]
list3 = [10, 'a']

很多人会错误地预计list1等于[10],list3等于[‘a’],认为extendList函数的list参数在每一次函数被调用时都会被设置为默认值[]
但是,真实的情况是,默认的list只在函数定义的时候被创建一次。之后不指定list参数地调用extendList函数时,使用的都是同一个list。这是因为带默认参数的表达式是在函数定义的时候被计算的,而不是在函数调用时。
简单的说:一个函数参数的默认值,仅仅在该函数定义的时候,被赋值一次。

Part 2

可是我记得参数是整形的时候不是这样的啊,试一下看看

1
2
3
4
5
6
7
8
9
10
11
def addNumber(val, num=5):
num += val
return num
num1 = addNumber(10)
num2 = addNumber(123, 0)
num3 = addNumber(9)
print "num1 = %s" % num1
print "num2 = %s" % num2
print "num3 = %s" % num3

执行结果:

1
2
3
num1 = 15
num2 = 123
num3 = 14

果然,这一次,每次调用的时候,num的初始值都是5。这又是什么原因呢?
解释:这就牵扯出了Python中的又一对概念———可变对象与不可变对象。列表是可变对象,对于列表的修改,是直接在原值上进行的。而整型是不可变对象,当Python中不可变对象的值发生变化时,只是改变了该对象的引用。譬如这里执行 num += val 时,其实并没有改变num变量原来对一个的那块内存的值,只是又开辟了一块内存,赋值为num+val,然后将num指向了这块地址。
我们可以用id()方法,输出变量的内存地址,就比较直观了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
def extendList(val, list=[]):
print id(list)
list.append(val)
print id(list)
return list
list1 = extendList(10)
list2 = extendList(123, [])
list3 = extendList('a')
print "list1 = %s" % list1
print "list2 = %s" % list2
print "list3 = %s" % list3
def addNumber(val, num=5):
print id(num)
num += val
print id(num)
return num
print "###########"
num1 = addNumber(10)
num2 = addNumber(123, 0)
num3 = addNumber(9)
print "num1 = %s" % num1
print "num2 = %s" % num2
print "num3 = %s" % num3

输出结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
56516424
56516424
57247112
57247112
56516424
56516424
list1 = [10, 'a']
list2 = [123]
list3 = [10, 'a']
###########
51471688
51471448
51471808
51474832
51471688
51471472
num1 = 15
num2 = 123
num3 = 14

可以看到,
第一次和第三次调用addNumber()时,num的默认地址是相同的,但赋值操作之后,地址发生了变化。但对于extendList(),是在list的原始地址上直接修改的。

欢迎打赏!