python list深度浅度copy

面试题之一。

[python]   view plain  copy
  1. def func1(p):  
  2.     p = p + [1]  
  3.   
  4. def func2(p):  
  5.     p += [1]  
  6.   
  7. p1 = [1,2,3]  
  8. p2 = [1,2,3]  
  9. func1(p1)  
  10. func2(p2)  
  11. print p1  
  12. print p2  
结果:

我以为像这种传参作为局部变量,因为都不会影响外部的list,所以答案应该是p1 =[1,2,3] ,p2=[1,2,3],然而

[python]   view plain  copy
  1. >>>   
  2. [123]  
  3. [1231]  
  4. >>>   


又一次被面试官虐杀,不甘心的我查找了python 局部变量:

[python]   view plain  copy
  1. x = [1,2,3]  
  2.   
  3.   
  4. def func(x):  
  5.     print "local! original x = ",x  
  6.     x  = [1]  
  7.     print "local! now x = ",x  
  8.   
  9.   
  10. func(x)  
  11. print "global! x = ",x  
结果:

[python]   view plain  copy
  1. local! original x =  [123]  
  2. local! now x =  [1]  
  3. global! x =  [123]  
没错啊,我还记得要用全局变量得加global x 之类的语句呢。


为了保险起见,加一个id(),查查看对象是不是同一个先:

[python]   view plain  copy
  1. x = [1,2,3]  
  2. print "before func(), global! x = ",x,"id(x) = ",id(x)  
  3.   
  4. def func(x):  
  5.     print "in func(), local! original x = ",x,"id(x) = ",id(x)  
  6.     x  = [1]  
  7.     print "in func(), local! now x = ",x,"id(x) = ",id(x)  
  8.   
  9. func(x)  
  10. print "after func(), global! x = ",x,"id(x) = ",id(x)  
结果:
[python]   view plain  copy
  1. before func(), global! x =  [123] id(x) =  46798728  
  2. in func(), local! original x =  [123] id(x) =  46798728  
  3. in func(), local! now x =  [1] id(x) =  46789512  
  4. after func(), global! x =  [123] id(x) =  46798728  

恩,可以看到,全局变量中的id(x) = 46798728,x进到func()中,因为执行了x = [1],才变成id(x) = 46789512。(合情合理)

这也说明python的确是传引用入函数。(然并卵)


利用id(x),查看下x = x + [1]对象是怎么变化的吧:

[python]   view plain  copy
  1. x = [1,2,3]  
  2. print "before func(), global! x = ",x,"id(x) = ",id(x)  
  3.   
  4. def func(x):  
  5.     print "in func(), local! original x = ",x,"id(x) = ",id(x)  
  6.     x  = x + [1]  
  7.     print "in func(), local! now x = ",x,"id(x) = ",id(x)  
  8.   
  9. func(x)  
  10. print "after func(), global! x = ",x,"id(x) = ",id(x)  
结果:

[python]   view plain  copy
  1. before func(), global! x =  [123] id(x) =  46339976  
  2. in func(), local! original x =  [123] id(x) =  46339976  
  3. in func(), local! now x =  [1231] id(x) =  46390664  
  4. after func(), global! x =  [123] id(x) =  46339976  
啊,x = x + [1],是新建了一个对象,id(x) =  46390664。


利用id(x),查看下x += [1]对象是怎么变化的吧

[python]   view plain  copy
  1. x = [1,2,3]  
  2. print "before func(), global! x = ",x,"id(x) = ",id(x)  
  3.   
  4. def func(x):  
  5.     print "in func(), local! original x = ",x,"id(x) = ",id(x)  
  6.     x += [1]  
  7.     print "in func(), local! now x = ",x,"id(x) = ",id(x)  
  8.   
  9. func(x)  
  10. print "after func(), global! x = ",x,"id(x) = ",id(x)  
结果:

[python]   view plain  copy
  1. before func(), global! x =  [123] id(x) =  46536584  
  2. in func(), local! original x =  [123] id(x) =  46536584  
  3. in func(), local! now x =  [1231] id(x) =  46536584  
  4. after func(), global! x =  [1231] id(x) =  46536584  
啊,id(x)全程一样,x += [1],python直接就在原对象上操作,还真是够懒的说。


利用id(x),查看下x.append([1])对象时如何变化的吧

[python]   view plain  copy
  1. x = [1,2,3]  
  2. print "before func(), global! x = ",x,"id(x) = ",id(x)  
  3.   
  4. def func(x):  
  5.     print "in func(), local! original x = ",x,"id(x) = ",id(x)  
  6.     x.append([1])  
  7.     print "in func(), local! now x = ",x,"id(x) = ",id(x)  
  8.   
  9. func(x)  
  10. print "after func(), global! x = ",x,"id(x) = ",id(x)  
结果:

[python]   view plain  copy
  1. before func(), global! x =  [123] id(x) =  47191944  
  2. in func(), local! original x =  [123] id(x) =  47191944  
  3. in func(), local! now x =  [123, [1]] id(x) =  47191944  
  4. after func(), global! x =  [123, [1]] id(x) =  47191944  
啊,id(x)全程一样,看来list的属性方法都是在原对象上操作的吧,我记得list.sort()也是,待会要验证的list.extend()估计也是。



利用id(x),查看下x.extend([1])对象时如何变化的吧

[python]   view plain  copy
  1. x = [1,2,3]  
  2. print "before func(), global! x = ",x,"id(x) = ",id(x)  
  3.   
  4. def func(x):  
  5.     print "in func(), local! original x = ",x,"id(x) = ",id(x)  
  6.     x.extend([1])  
  7.     print "in func(), local! now x = ",x,"id(x) = ",id(x)  
  8.   
  9. func(x)  
  10. print "after func(), global! x = ",x,"id(x) = ",id(x)  
结果:

[python]   view plain  copy
  1. before func(), global! x =  [123] id(x) =  48437128  
  2. in func(), local! original x =  [123] id(x) =  48437128  
  3. in func(), local! now x =  [1231] id(x) =  48437128  
  4. after func(), global! x =  [1231] id(x) =  48437128  
果然id(x)全程一样。



话说list.append()是追加,extend()是拓展,他们的区别大概就是:

[python]   view plain  copy
  1. >>> a = [1,2,3]  
  2. >>> b = [4,5,6]  
  3. >>> c = [7,8,9]  
  4. >>> a.append(b)  
  5. >>> a  
  6. [123, [456]]  
  7. >>> c.extend(b)  
  8. >>> c  
  9. [789456]  
  10. >>>  
看了上面的几段代码,聪明的你应该也能看出来:

list1 += list2  等价于 list1.extend(list2),这是证据

源代码地址:http://svn.python.org/view/python/trunk/Objects/listobject.c?view=markup

[python]   view plain  copy
  1. 913 static PyObject *  
  2. 914 list_inplace_concat(PyListObject *self, PyObject *other)  
  3. 915 {  
  4. 916     PyObject *result;  
  5. 917   
  6. 918     result = listextend(self, other); //+=果然用了listextend()  
  7. 919     if (result == NULL)  
  8. 920         return result;  
  9. 921     Py_DECREF(result);  
  10. 922     Py_INCREF(self);  
  11. 923     return (PyObject *)self;  
  12. 924 }  


利用id(x),查看下global x下,对象的变化吧:

[python]   view plain  copy
  1. x = [1,2,3]  
  2. print "before func(), global! x = ",x,"id(x) = ",id(x)  
  3.   
  4. def func():  
  5.     global x  
  6.     print "in func(), local! original x = ",x,"id(x) = ",id(x)  
  7.     x = x + [1]  
  8.     print "in func(), local! now x = ",x,"id(x) = ",id(x)  
  9.   
  10. func()  
  11. print "after func(), global! x = ",x,"id(x) = ",id(x)  
结果:

[python]   view plain  copy
  1. before func(), global! x =  [123] id(x) =  47781768  
  2. in func(), local! original x =  [123] id(x) =  47781768  
  3. in func(), local! now x =  [1231] id(x) =  47795720  
  4. after func(), global! x =  [1231] id(x) =  47795720  
啊,global就保证了,即使我的变量x在函数中指向对象变了,外部的x也会指向新的对象。


回到面试题:

[python]   view plain  copy
  1. def func1(p):  
  2.     p = p + [1]  
  3.   
  4. def func2(p):  
  5.     p += [1]  
  6.   
  7. p1 = [1,2,3]  
  8. p2 = [1,2,3]  
  9. func1(p1)  
  10. func2(p2)  
  11. print p1  
  12. print p2  

p1传入func1(),因为+操作,生成一个新的对象,但没有return给外部的p1,所以外部的p1=[1,2,3]。

p2传入func2(),因为+=操作,就是list.extend(),操作,在原对象操作,所以p2=[1,2,3,1]。



吐槽下:

其实python在函数中参数是传引用的,如果一个对象obj进到函数中,被改变,那无论在哪里这个obj就是被改变了,并没有什么副本什么的。

那为什么有些时候看起来,函数中obj怎么被改变,外部的obj都岿然不动,啊,因为这个被改变后的obj不是原来的它了。比如x = x + [1],新的x真的是原来传进来的x吗?不是的。此时的x是新的对象了(看id就知道了),先前传进来的x,并没有被改变。

一点浅见,求打脸。



总结:

1、list + 创建一个新的对象。

2、list的 += 和 list.extend(),等价,都是在原对象上操作。

3、list.append(),也是在原对象上操作。

4、global,全局变量,嗯,不错(这算什么总结嘛)。


猜你喜欢

转载自blog.csdn.net/weixin_41071202/article/details/80216949