地址赋值
先看一个例子,加入我现在定义了一个列表lst1,然后我让另一个列表lst2=lst1,现在如果我对列表lst2进行修改,lst1是否会发生变化?
lst1=[1,2,3]
lst2=lst1
print(lst2)
#out: [1,2,3]
lst2.append(4)
print(lst1)
#out: [1,2,3,4]
可以看到当我们对lst2进行修改的时候,lst1也发生了变化。这是因为当我们在进行lst2=lst1操作时,其实并不是属于拷贝操作,而只是把lst1所指向的列表地址赋给了lst2,而并没有开辟出新的内存空间。所以lst1,lst2实际上都是表示的指针的概念。这种类型的lst2=lst1实质上是地址赋值
用图示来表示下就是这样
浅拷贝
如果我们需要开辟新的内存空间,即让lst2与lst1独立,我们可以使用**lst.copy()**函数
lst1=[1,2,3]
lst2=lst1.copy()
lst2.append(4)
print(lst1,lst2)
#out: [1, 2, 3] [1, 2, 3, 4]
可以看到这次对于lst2的修改,就不会改动lst1了。因为用了copy()函数,我们为lst2开辟了一串新的内存空间,将lst1指向的列表中的内容都复制到了lst2指向的新的内存空间中。这样的我们称为进行了拷贝
接下来再让我们来看一个例子:假如我现在定义一个列表,在这个列表lst1中嵌套了一个列表lst_t,然后我让lst2=lst1,copy(),从上一个例子我们知道lst2已经对lst1进行了拷贝。那如果我对lst2中的lst_t进行操作,lst1会发生改变吗?
lst1=[1,2,3,['a','b','c']]
lst2=lst1.copy()
lst2.append(4)
lst2[3].append(4)
print(lst1)
print(lst2)
#out:
#[1, 2, 3, ['a', 'b', 'c', 4]]
#[1, 2, 3, ['a', 'b', 'c', 4], 4]
当我们对lst2进行append操作时,lst1并没有发生变化,可是当我们对lst2的lst_t进行操作时,lst1中的lst_t却发生了相同的变化。这就是深浅拷贝的问题造成的。
想要知道为什么明明使用了copy,但是lst1还是会发生变化,就要首先了解嵌套列表的存储方式。由于列表是一个可变字段,所以当一个列表嵌套到另一个列表中的时候,父列表并不是把子列表的内容都加入了内存空间,而是添加了一个记录子列表位置的指针。所以他的结构应该是这样的
所以当lst2对lst1进行拷贝的时候,是将lst1内存空间的数据都拷贝了下来,而在lst1中是存储的位置指针,在lst2中同样拷贝为位置指针。所以lst1与lst2的结构就应该是这样
lst2虽然拷贝了lst1的内容,但是lst2[3]与lst1[3]都是指向同一个字列表的指针。这就是我们所说的浅拷贝。
深拷贝
那如果我们想要连嵌套列表的内容也都重新拷贝一份,就要使用到深拷贝。使用深拷贝需要导入copy模块,使用copy模块中的deepcopy()函数
lst1=[1,2,3,['a','b','c']]
lst2=copy.deepcopy(lst1)
lst2.append(4)
lst2[3].append(4)
print(lst1)
print(lst2)
#out:
#[1, 2, 3, ['a', 'b', 'c']]
#[1, 2, 3, ['a', 'b', 'c', 4], 4]
使用深拷贝之后lst2会完全拷贝lst1中的内容以及lst1中指针所指向的内容。就是说无论lst1跟lst2是两个完全独立的个体了,对其中一个进行任何操作都不会影响另一个。
列表的切片lst[:],与sorted()排序方法都是借用临时的存储空间,但是可以赋值给另外的函数,我们探究下者两者是属于地址赋值还是深浅拷贝。
从之前的例子,我们已经可以总结出如何实验探究一个赋值是属于地址赋值还是深浅拷贝。
我们令lst2=lst1;如果对lst2直接进行修改,lst1也同样发生改变,这就是地址赋值;如果不发生改变,对lst2的嵌套子列表进行修改,如果lst1发生改变,则属于浅拷贝;如果lst1不发生变化,就属于深拷贝。下面我们来实验
列表切片:
lst1=[1,2,3,['a','b','c']]
lst2=lst1[:]
lst2.append(4)
lst2[3].append(4)
print(lst1)
print(lst2)
#out:
#[1, 2, 3, ['a', 'b', 'c', 4]]
#[1, 2, 3, ['a', 'b', 'c', 4], 4]
可以看到对lst2直接进行append(),lst1没有发生改变,所以不是地址赋值。而对lst2[3]进行append(),lst1却同样发生了变化,所以可以知道列表的切片赋值属于浅拷贝。
sorted()排序
lst1=[[1,2],[3,4],[5,6]]
lst2=sorted(lst1)
lst2.append(4)
lst2[0].append('a')
print(lst1)
print(lst2)
#out:
#[[1, 2, 'a'], [3, 4], [5, 6]]
#[[1, 2, 'a'], [3, 4], [5, 6], 4]
因为不是统一类型无法排序,所以lst1中元素全选取的列表
可以看到直接对lst2进行append(),lst1没有发生变化,对lst2子列表进行append(),lst1发生了变化,所以sorted()同样也属于浅拷贝。
其实深浅拷贝是由于没有另外定义指针所产生的问题,用一句话来判断是深浅拷贝的哪一种,那就是是否拷贝指针的指向空间。实际中多数拷贝都属于浅拷贝。地址赋值也是浅拷贝的一种特殊形式,在只有一个指针元素的时候发生。通常在有嵌套列表的时候要注意下深浅拷贝问题。