一个老问题:
1 def func(defau=[]): 2 defau.append(1) 3 return defau 4 5 print(func())#print[1] 6 print(func())#print[1,1] 7 print(func())#print[1,1,1]
学python时候应该都遇到过这个问题,为什么?一般的说法是把这个可变的默认参数和函数绑定在一块了
但是,怎么绑定的???
看python文档[1],里面对def的解释:
A function definition is an executable statement. Its execution binds the function name in the current local namespace to a function object .
一个可执行的声明,在现有的名称空间中把函数名字和函数对象绑定在一块了。
也就是说,这种绑定只有在def声明执行的时候才会发生,只有在def语句执行的时候,才会把函数名字和一个新的函数对象绑定在一块[2]。
那么,这些和默认参数有什么关系?
这默认参数的关系就是,只有当这种绑定发生的时候,默认参数才会得到赋值,换句话说,只有在def声明执行的时候,默认参数所引用的对象才会和函数对象结合起来。
所以,在上面的例子中,因为def语句只执行了一次,所以几次调用函数func其实都是执行的同一个函数对象,使用的同一个list:
1 def func(defau=[]): 2 defau.append(1) 3 return defau 4 5 print(id(func()))#print 1756350390856 6 print(id(func()))#print 1756350390856 7 print(id(func()))#print 1756350390856
看大神laike9m的博客[3]的时候,发现其实可以利用这一点对python的性能进行优化的。
我们都知道,python中名称的查找顺序为LEGB,也就是local->enclosing->global->builtin,如果我们把一个全局变量赋值给一个函数的默认参数,把一个全局变量变为一个函数的局部变量,那么在查找链中,就不用进行到global,直接在local域中就可以得到需要的变量了:
1 import timeit 2 setup1=""" 3 import math 4 def cal(): 5 math.sin(1)+math.cos(1) 6 """ 7 setup2=""" 8 import math 9 def cal2(sin=math.sin,cos=math.cos): 10 sin(1)+cos(1) 11 """ 12 t1=timeit.Timer(setup1,"print('setup1')") 13 t2=timeit.Timer(setup2,"print('setup2')") 14 print(t1.timeit(100)) 15 print(t2.timeit(100)) 16 #setup1 17 #0.0003371067712671346 18 #setup2 19 #8.921092541729796e-05
可以看到,在执行100遍的情况下,运行时间相差一个量级
参考资料:[1]python 文档