考虑一个场景:在该场景中,我们有各种列表的颜色,每个颜色代表假定颜色类的一个
实例。我们让标识符a表示现有的颜色列表。在这个应用中,我们希望创建一个名为b的新列
表,复制一份b列表。达到修改、删除、添加b中的颜色而不影响到a列表。
1、第一种情况:(创建别名)
假设a = [“red”,“blue”,“green”],如果执行命令b = a,这样仅仅是创建了这个列表的一个别名,a和b是指向同一个列表对象,它们的内存地址是一样的。
a = ["red","blue","green"]
b = a
print(id(a),id(b))#2253502304840 2253502304840
a[0] = "yellow"
print(a,b)
#['yellow', 'blue', 'green'] ['yellow', 'blue', 'green']
因为是a和b代表的是同一个列表对象,那么修改a其实就是在修改b.
2、第二种情况:(浅拷贝)
我们用构造函数创建一个新的列表实例:b = list(a),这种方式就被称为浅拷贝。
我们写一个代码来分析一下:
a = ["red","blue","green"]
b = list(a)
print(id(a),id(b))#2161162736200 2161193156104
print(id(a[0]),id(b[0]))#2161163276144 2161163276144
a[0] = "yellow"
print(a,b)#['yellow', 'blue', 'green'] ['red', 'blue', 'green']
print(id(a[0]),id(b[0]))#2161193327984 2161163276144
我们通过代码打印的结果来分析一下:
- a、b的内存地址是不一样的,这个因为a和b是不同的对象,a和b自然就是占用不同的内存空间的。
- 但是我们发现a、b的第一个元素的内存地址为什么是一样的呢?其实这个主要是python的list采用的是引用数组,这里虽然a和b是两个截然不同的对象,但是它们其中的元素却是相同的元素,只是在a和b中是两个指向同一元素地址空间的别名。
- 相信有的同学在第二条可能会有点迷惑,既然说a和b里的元素其实是指向的同一个元素,那么为什么在修改之后,打印出来的却是两个不同的元素呢?以x = 2为例,y = x,假设x = x+1,那么x=3,y=2,这里不也是吗,x和y指向同一个元素2,但是修改了x之后,y的值却是不会变化的,因为这里的元素是不可变对象,x = x+1之后,x已经不是原来的那个x了。看下面的不可变对象a[0]。
import copy
a = [[1,2,3],"red","blue","green"]
# b = copy.deepcopy(a)
b = list(a)
print(id(a),id(b))
print(id(a[0]),id(b[0]))
a[0].append(4)
print(a,b)#[[1, 2, 3, 4], 'red', 'blue', 'green'] [[1, 2, 3, 4], 'red', 'blue', 'green']
print(id(a[0]),id(b[0]))#2226783836168 2226783836168
- 因为a[0]是一个列表,是不可变对象,可以对a[0]进行修改,我们看到之后的内存地址也不会发生变化。
注:所以浅拷贝只拷贝一层的说法也是由此而来。
3、第三种情况:(深拷贝)
直接看代码:
import copy
a = [[1,2,3],"red","blue","green"]
b = copy.deepcopy(a)
# b = list(a)
print(id(a),id(b))
print(id(a[0]),id(b[0]))
a[0].append(4)
print(a,b)#[[1, 2, 3, 4], 'red', 'blue', 'green'] [[1, 2, 3], 'red', 'blue', 'green']
print(id(a[0]),id(b[0]))#2453682903112 2453682902920
- 从上面的代码就可以看出,这里a和b的元素就不存在别名的关系了,它们是实实在在的两个对象,对一个操作并不会对另一个产生影响。
注:善于尝试的同学可能会发现,上面深拷贝的这个代码print(id(a[1]),id(b[1]))的内存地址是一样的,这里主要是数据池对字符串造成的影响,有兴趣的可以自己去了解一下。