好久没更新博客了,过了一个春节,人也变懒了。切入正题,用多了C/C++,用惯了指针,突然跳到一个没有指针的世界确实有点不适应。只想要简单拷贝一个对象,然后奇怪的事情出现了:
第一个例子:
a=1 b=a a=2 print 'a is:',a print 'b is:',b
a is: 2 b is: 1很简单么,和C也没多大区别。再试试列表
a=[1,2,3,4] b=a a[0]=0 print 'a is:',a print 'b is:',b
a is: [0, 2, 3, 4] b is: [0, 2, 3, 4]是不是很诧异,怎么拷贝的对象变化,要了解这个问题我们必须了解一下python中数据存储的一个基本原理:万物皆对象。python中不存在所谓的传值调用,一切数据的传递都是对象的引用。所以普通的拷贝仅仅只是对象的引用(这个不需要解释吧)。但是这里又会出现一个疑问,为什么第一个例子中b没有随a的改变而改变?这里我们需要了解一下python中数据的两种类型:不可变类型和可变类型。其中数字,字符串和元组都是不可变类型,列表和字典属于可变类型。那么前面的疑问也很好解释了,a只是指向了内容为2的对象,原来的1所在的对象没有被改变,所以b还是指向的1所在的对象。而例子2中,列表是可变类型,所以a改变的是[1,2,3,4]所在的对象,由于是引用传递,那么b也就相应改变(因为b指向的也是[1,2,3,4]所在的对象)。有点糊涂吧,多理解理解就懂了。那么我们需要拷贝一个对象(当然是可变类型啦,因为不可变类型的话前面的方法就可以解决了)怎么办呢?继续看下面的例子:
a=['pitter',['saveing',100]] b=a[:] a[0]='zhuzhu' a[1][1]=200 print 'a is:',a print 'b is:',b a is: ['zhuzhu', ['saveing', 200]] b is: ['pitter', ['saveing', 200]]貌似效果不错,至少成功了一半。这里a[0]成功变成了‘zhuzhu’,而b[0]还是‘pitter’,但是b[1][1]却不受控制地变成了200,这是我们不希望的。为什么?虽然我们成功地拷贝了对象,但是还不完全,可以这样说对象是新的(不是引用哦,新的对象),但是内容是旧的(可以理解为内容引用了,a[0],a[1]引用了),而a[0]是不可变的字符串,a[1]是可变的列表,所以它改变不了b[0]的对象,但却可以改变b[1]的对象。是不是有点晕了,哈哈,就是这样,你可以使用id()看下结果。
a=['pitter',['saveing',100]] b=a[:] print id(a),id(a[0]),id(a[1]),id(a[1][0]),id(a[1][1]) print id(b),id(b[0]),id(b[1]),id(b[1][0]),id(b[1][1]) a[0]='zhuzhu' a[1][1]=200 print id(a),id(a[0]),id(a[1]),id(a[1][0]),id(a[1][1]) print id(b),id(b[0]),id(b[1]),id(b[1][0]),id(b[1][1]) print 'a is:',a print 'b is:',b 38483528 38299936 38483448 38472288 33468508 38492440 38299936 38483448 38472288 33468508 38483528 38472320 38483448 38472288 33469292 38492440 38299936 38483448 38472288 33469292 a is: ['zhuzhu', ['saveing', 200]] b is: ['pitter', ['saveing', 200]]可以看到,上面的拷贝还不是完全意义上的拷贝,这里我们称之为浅拷贝。浅拷贝产生的方式有:(1)完全切片,如[:](2)工厂函数,如list()(3)copy模块的copy函数。这时我们离目标越来越近了,既然有浅拷贝,那么就应该有深拷贝,copy模块的deepcopy()就可以做到这一点。
import copy a=['pitter',['saveing',100]] b=copy.deepcopy(a) print id(a),id(a[0]),id(a[1]),id(a[1][0]),id(a[1][1]) print id(b),id(b[0]),id(b[1]),id(b[1][0]),id(b[1][1]) a[0]='zhuzhu' a[1][1]=200 print id(a),id(a[0]),id(a[1]),id(a[1][0]),id(a[1][1]) print id(b),id(b[0]),id(b[1]),id(b[1][0]),id(b[1][1]) print 'a is:',a print 'b is:',b 39335496 39151904 39335416 39324256 30191708 39344408 39151904 39367584 39324256 30191708 39335496 39324320 39335416 39324256 30192492 39344408 39151904 39367584 39324256 30191708 a is: ['zhuzhu', ['saveing', 200]] b is: ['pitter', ['saveing', 100]]大功告成,是不是有点迷糊的。正常,多多练习就理解了。最后,不知道你发现没有,对于可变对象,如果id()值相等代表的是引用,不相等代表的是真正的拷贝。比对下浅拷贝和深拷贝中id(a[1]),id(b[1])的值,这时你应该恍然大悟吧。