[python]一个关于默认参数的老问题和一个有关优化的新问题

一个老问题:

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 文档

     [2]Default parameter values in python

              [3]pythony优化函数执行的技巧

原文地址:https://www.cnblogs.com/fcyworld/p/6846354.html