python 当list,dic作为默认参数的正确写法

来源:互联网 发布:模拟加密狗软件 编辑:程序博客网 时间:2024/05/21 08:59

面试题之一。

def extendList(val,list=[]):    list.append(val)    return listlist1 = extendList(10)list2 = extendList(123,[])list3 = extendList('a')print list1print list2print list3
结果:

我猜应该是[10],[123],['a'],但实际上是:

[10, 'a'][123][10, 'a']

被面试官虐杀的我,查找了资料了,看看下面的有问题的代码:

def foo(a, b =[]):    b.append(a)    print bfoo(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): 48881992b.__defaults__: ([5],)id(b.__defaults__[0]): 48881992********************id(x): 48881992b.__defaults__: ([5, 5],)id(b.__defaults__[0]): 48881992********************id(x): 48753672b.__defaults__: ([5, 5],)id(b.__defaults__[0]): 48881992********************id(x): 48754056b.__defaults__: ([5, 5],)id(b.__defaults__[0]): 48881992

看来这个list当默认参数值时,还需要特别注意呢。

为了避免类似问题代码的情况,官方文档的写法是:

def foo(a, b=None):    if b is None:        b = []    b.append(a)    print bfoo(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





1 0