list[] 和 list[:] 的理解
list“赋值”时会用到list2 = list1 或者 list2[:] = list1,前者两个名字指向同一个对象,后者两个名字指向不同对象。理解如下:
首先,python中没有赋值的说法,只有名称到对象的引用;
list2 = list1是把list1所指的对象绑定到名字list2上,没有产生新list,只是新增了一个引用;
正因为两个名称指向的同一个对象,所以修改list1,那么list2也会改变;
通俗理解:以前有一套三室一厅的房子,户主叫list1。后来list1和list2结婚,房产证上户主的名字加了一个,但房子还是只有一套。list1如果把客厅刷成了蓝色,那list2回家的时候会发现客厅是蓝色的了。
而list2 = list1[:]则是把list1通过切片运算取得的新list对象绑定到list2上,产生了新list,名称和引用也不同,所以,修改其中一个,另一个不会变。
注意,这里的切片是浅拷贝,参考下面的笔记。
通俗理解:从前有一套三室一厅的房子,户主叫list1。后来有个叫list2的,觉得list1的房子不错,于是买了一套相同户型的,也装修得一模一样。后来list1把自己的客厅刷成了蓝色,list2回家发现自己家的客厅还是白色,并没有变成蓝色。
浅拷贝(shallow copy)
浅拷贝会创建新对象,其内容是原对象的引用。
浅拷贝有三种形式:切片操作,工厂函数,copy模块中的copy函数。
比如对:a = [1,2,3,"hello",["python","C++"]]
1、切片操作:b = a[:] 或者 b = [each for each in a]
2、工厂函数:b = list(a)
3、copy函数:b = copy.copy(a)
浅拷贝产生的b不再是a了,使用is可以发现他们不是同一个对象,使用id查看,发现它们也不指向同一片内存。但是当我们使用 id(x) for x in a 和 id(x) for x in b 时,可以看到二者包含的元素的地址是相同的。
在这种情况下,a和b是不同的对象,修改b理论上不会影响a。
比如b.append([4,5]):
b = a[:]
b.append("ADD")
a [1, 2, 3, 'hello', ['python', 'C++']] b [1, 2, 3, 'hello', ['python', 'C++'], 'ADD']
注:浅拷贝之所以称为浅拷贝,是它仅仅只拷贝了一层,在a中有一个嵌套的list,如果我们修改了它,情况就不一样了。
a[4].append("C")。查看b,可发现b也发生了变化。这是因为,我们修改了嵌套的list。修改外层元素,会修改它的引用,让它们指向别的位置,修改嵌套列表中的元素,列表的地址并为发生变化,指向的都是同一个位置。
深拷贝(deep copy)
深拷贝只有一种形式,copy模块中的deepcopy函数。
和浅拷贝对应,深拷贝拷贝了对象的所有元素,包括多层嵌套的元素。因而,它的时间和空间开销要高。
同样对上述list类型的a,若使用b = copy.deepcopy(a),再修改b将不会影响到a了。即使嵌套的列表具有更深的层次,也不会产生任何影响,因为深拷贝出来的对象根本就是一个全新的对象,不再与原来的对象有任何关联。