最近看到的文章中提到的一个神奇的现象。。。
python与其处理传入参数的方式
首先我们来看下面的程序:
1 | def extend_list(val, l=[]): |
这个list1和list2的输出是什么呢?
一般来说应该会认为是:
1 | >>> list1 |
然而实际上答案却不是这样的。。。
1 | list1 |
会发现,list1指向的list在第二次extend_list执行的过程中,依然参与了函数的运算?这是为什么呢。。。
python的参数
对于python这类程序来说,变量不是[指针]而是[句柄],也就是说,python中的变量都只是一个指向内存位置的id。然而每次在定义一个函数的时候,可能是为了节约空间,所有的mutable对象都只会在定义的时候初始化一次,这就意味着,无论我们调用多少次这个函数,返回值中的list始终是同一个,所以当我们调用函数次数越多,list中的元素也就越多。
为了检验这一点,我们使用id()来检查这一点:
1 | def extend_list(val, l=[]): |
从这里可以看出,我们的val值对应的id始终是不一样的,而这个l始终指向了同一个内存地址。因此此时修改list1,list2或者再次调用函数,都将会对他们指向的那个list造成影响。
避免办法
这个问题官方是有提到的,官方不建议那mutable作为参数,取而代之的是:
1 | def f(a, L=None): |
使用不可变参数None作为传入。
或者,我们可以在传入时产生一个副本(参考知乎大佬)
1 | def foo(bar=[]): |
这样的话每次bar对应的都是另一个list,同样可以避免问题。