我们知道在python中一切皆对象。在python世界中可以把对象大体分成两大类:
所谓不可变对象就是对象的内容不能改变。给人感觉有点像c++的const修饰的对象。但是我们可以对一个不可变对象进行赋值(写操作),这时候这个不可变的对象就指向了一个新的地址空间,指向的内容也更新成新赋值的内容
可变对象就是对对象本身的修改(写操作)是直接作用于对象自身的,不会产生新的对象
通过内置函数id()来获取变量的地址,下面举例说明:
num = 1
print "num addr:",id(num)
num += 2
print "num addr:",id(num)
num addr: 74024648
num addr: 74024624
执行num += 2给不可变对象num赋值的时候会产生一个新的地址空间,并用新的内容填充这块地址空间,正如上面的输出所示,此时的num的地址已经改变
下面再看一个
listnum = [1,23,2]
print "list addr:",id(listnum)
listnum.append(11)
print "list addr:",id(listnum)
print listnum
list addr: 79300608
list addr: 79300608
[1, 23, 2, 11]
list是可变对象,修改一个可换对象可直接作用于这个可变对象,所以最终的list就插入了一个元素
num = 1
num_bk = num
print "num addr:%d num_bk addr:%d" %(id(num),id(num_bk))
listnum = [1,23,2]
listnum_bk = listnum
print "listnum addr:%d listnum_bk addr:%d"%(id(listnum),id(listnum_bk))
num addr:77104840 num_bk addr:77104840
listnum addr:79104000 listnum_bk addr:79104000
可以看出不管是可变对象还是不可变对象,赋值后两个对象的地址是相同的。也证实了上面的结论。所以问题的关键就变成了我们对新赋值的对象进行修改是否会影响到原对象的值。这个问题似乎回到了上面的对可变对象和不可变对象的写操作的问题。
通过上面的讲解我们知道:
到此我们应该很容易知道:
def UpdateNum(num):
print "begin update num addr:",id(num)
num += 2
print "after uodate num addr:",id(num)
num = 1
print "begin call function num add:",id(num)
UpdateNum(num)
print "after call function num add:",id(num)
begin call function num add: 77104840
begin update num addr: 77104840
after uodate num addr: 77104816
after call function num add: 77104840
这个问题这样表述可能有点问题,但是意思可能都明白。就是想实现,传入的是可变对象时,函数内部的修改不会影响到原来的对象;传入是不可变对象,函数内部的修改能够影响到原来的对象
对于后者的解决方案比较简单,就是函数通过返回值回传这个新的对象,调用方用原来的对象来接收这个新的对象就达到了这个效果。但是要实现可变对象的传值问题就要借助于复制的内置函数了。这个介绍两个拷贝函数:copy(浅拷贝)、deepcopy(深拷贝)
listnum = [1,2,[11,22],3]
listcopy = copy.copy(listnum)
listcopy.append(4)
print "list value :", listnum
print "listcopy value:", listcopy
listnum[2].append(33)
print "list value :", listnum
print "listcopy value:", listcopy
list value : [1, 2, [11, 22], 3]
listcopy value: [1, 2, [11, 22], 3, 4]
list value : [1, 2, [11, 22, 33], 3]
listcopy value: [1, 2, [11, 22, 33], 3, 4]
可以看到我们子对象listnum[2]的修改会作用到复制的两个对象,而深拷贝就是解决这个问题的
listnum = [1,2,[11,22],3]
listcopy = copy.deepcopy(listnum)
listcopy.append(4)
print "list value :", listnum
print "listcopy value:", listcopy
listnum[2].append(33)
print "list value :", listnum
print "listcopy value:", listcopy
list value : [1, 2, [11, 22], 3]
listcopy value: [1, 2, [11, 22], 3, 4]
list value : [1, 2, [11, 22, 33], 3]
listcopy value: [1, 2, [11, 22], 3, 4]
可以看到所有的修改都是独立的
熟悉c++的同学可能对深浅拷贝理解起来更容易。深浅拷贝的区别就是对指针成员对象进行的是仅仅指针的复制还是对指针所指示的内存空间进行复制。仅复制指针的话,由于两个指针同时指向同一块内存,所以修改是同步的