python 默认参数问题的陷阱
python里面一个常见的陷阱就是函数的默认参数问题。如下:
deffunc(mylist=[]): mylist.append(1) returnmylist
以下的执行结果如下:
printfunc() printfunc() printfunc() printfunc(['a']) printfunc()
结果如下:
[1] [1,1] [1,1,1] ['a',1] [1,1,1,1]
如此结果,前面三个可以看出如果没有指定参数的话,每次调用函数时候,调用的mylist是同一个对象。这是因为函数的默认参数,是在代码编译成PyCodeObject的时候,就已经创建了对象指针,并且存在该函数的func_default内。以后在代码运行,调用函数的时候,如果没有指定参数的话,每次调用的话,该参数变量都是代码编译阶段的变量指针所指定的对象。
printfunc.func_default
此时结果就是:
([1,1,1,1],)
默认参数分为两种情况:
默认参数值是不可变对象
此时函数的func_default一直指向该不变对象,如果函数内部修改了该变量,那么该默认参数会指向一个新的不可变对象.
不过func_default不变。而每次调用函数都是读取func_default,因此每次执行都一样。
In[30]:deffunc2(var=1): ....:var+=1 ....:returnvar ....: In[31]:func2() Out[31]:2 In[32]:func2() Out[32]:2 In[34]:func2.func_defaults Out[34]:(1,)
默认参数是可变对象,比如list,dict,class等
这种情况下,如果在函数内修改了指针所指的对象(并未创建新的对象),那么func_default就会改变。这正是开始的mylist发生变化的原因。看下面的例子,:
In[35]:deffunc(mylist=[]): ....:mylist=[]#这里创建了新的对象, mylist.append(1) returnmylist In[44]:func() Out[44]:[1] In[45]:func.func_defaults Out[45]:([],)
由于创建了对象,mylist只是作为一个新建对象的别名存在,后面在修改已经与func_default无关了。
默认参数的一个应用
先看下面的一个经典的例子:
defouter(): res=[] foriinrange(4): definner(j): returnj*i res.append(inner) returnres print[m(2)forminouter()]
#简略版本:
defmultipliers(): return[lambdax:i*xforiinrange(4)] print[m(2)forminmultipliers()]
结果是[6,6,6,6],而不是[0,2,4,6],原因就是闭包的延迟绑定。另外函数绑定的是变量而不是绑定数值。当循环结束了,i的值已经是3,此时结果都是6.一个解决方法便是,使用默认参数绑定数值。如下改动:
defouter(): res=[] foriinrange(4): definner(j,i=i): returnj*i res.append(inner) returnres print[m(2)forminouter()]
#简略版本:
defmultipliers(): return[lambdax,i=i:i*xforiinrange(4)] print[m(2)forminmultipliers()]
这样的话,利用默认参数在代码编译的时候,便把参数写到函数的func_default中,就可以绑定0,1,2,3了。结果自然就是
[0,2,4,6]
这就是默认参数的一个应用。
上述还有一个生成器修改的方式
defmultipliers(): return(lambdax,i=i:i*xforiinrange(4))#修改成生成器 print[m(2)forminmultipliers()]