面试题之一。
def extendList(val,list=[]):
list.append(val)
return list
list1 = extendList(10)
list2 = extendList(123,[])
list3 = extendList('a')
print list1
print list2
print list3
结果:
我猜应该是[10],[123],['a'],但实际上是:
[10, 'a']
[123]
[10, 'a']
被面试官虐杀的我,查找了资料了,看看下面的有问题的代码:
def foo(a, b =[]):
b.append(a)
print b
foo(1)
foo(2)
结果想象中是:
>>>
[1]
[2]
>>>
实际上是:
>>>
[1]
[1, 2]
>>>
原来当list等可变类型作为默认参数时,仅仅在定义函数的时候(也就是执行def语句)被计算一次,有且仅有这么一次。
其它的时候无论调用几次函数,如果没有传参进来,就会一直用这个默认参数了。
看起来有点像是静态变量,静态变量的初始化也仅仅是执行一次。
为了验证下上述的几句话,我们需要先介绍下一些小工具:
id(obj),查看一个对象的id,如果两个对象的id是一样的,意味着他们是同一个对象。
func.__defaults__,是一个列表,存放着函数的默认参数的一个list,因为b()只有一个默认参数,那么b.__defaults__[0]就是指向b()的第一个默认参数x。
证据:
def a():
print 'a executed'
return []
def b(x=a()):
print 'id(x):',id(x)
x.append(5)
for i in range(2):
print '*'*20
b()
print 'b.__defaults__:',b.__defaults__
print 'id(b.__defaults__[0]):',id(b.__defaults__[0])
for i in range(2):
print '*'*20
b(list())
print 'b.__defaults__:',b.__defaults__
print 'id(b.__defaults__[0]):',id(b.__defaults__[0])
我们先预测结果:
1、因为默认函数只会在def执行的时候,被计算一次,所以结果应该只有一个:a executed。
2、两个for循环一共4次调用b(),前两次没有传参(那么x就用的是默认参数),后两次传参。
3、因为默认参数一直就这么一个对象,所以两个for循环中四个id(b.__defaults__[0])都是指向同一个id。
4、前两次调用b()中,id(x) == id(b.__defaults__[0]),后两次b()中,id(x)互相不同,且都异于id(b.__defaults__[0])。
实际结果是(完全正确):
a executed
********************
id(x): 48881992
b.__defaults__: ([5],)
id(b.__defaults__[0]): 48881992
********************
id(x): 48881992
b.__defaults__: ([5, 5],)
id(b.__defaults__[0]): 48881992
********************
id(x): 48753672
b.__defaults__: ([5, 5],)
id(b.__defaults__[0]): 48881992
********************
id(x): 48754056
b.__defaults__: ([5, 5],)
id(b.__defaults__[0]): 48881992
看来这个list当默认参数值时,还需要特别注意呢。
为了避免类似问题代码的情况,官方文档的写法是:
def foo(a, b=None):
if b is None:
b = []
b.append(a)
print b
foo(1)
foo(2)
总结:
1、当list、dic等可变类型作为函数默认参数并且调用函数时没有传参的时候,要注意list、dic并不会自己清空。
2、默认参数只在def语句被执行的时候计算一次。
3、如果想要的话,把默认参数当成静态变量(也就是全局变量)也是一种抖机灵的好思路呢。
参考文献:
官方文档:https://docs.python.org/2.7/tutorial/controlflow.html#default-argument-values
知乎某问题:http://www.zhihu.com/question/21924859
陷阱!python参数默认值:http://segmentfault.com/a/1190000000743526