对python中的浅拷贝和深拷贝的一些理解
抛出问题
在使用python
创建列表时,出现了一些问题,下面是我创建列表矩阵的三种方式:
#第一种方式
A = [0] * 3
for i in range(3):
A[i] = [0] * 3
print(A)
A[0][1] = 1
print(A)
#第二种方式
B = [[0] * 3] * 3
print(B)
B[0][1] = 1
print(B)
#第三种方式,使用列表推导式
C = [[0] * 3 for i in range(3)]
print(C)
C[0][1] = 1
print(C)
然而,运行出来的结果是这样的:
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[0, 1, 0], [0, 0, 0], [0, 0, 0]]
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[0, 1, 0], [0, 1, 0], [0, 1, 0]]
[[0, 0, 0], [0, 0, 0], [0, 0, 0]]
[[0, 1, 0], [0, 0, 0], [0, 0, 0]]
第二种方法将矩阵中中间一列的值全都改变了,显然矩阵A
和B
并不等价,查找资料后发现第二种方法只是对同一列表进行了三次引用。下面来解释一下浅拷贝和深拷贝的相关用法。
浅拷贝
我们通过代码来理解一下:
#下面介绍第一种浅拷贝方法,列表里的copy()方法
#创建一个一维列表,命名为x
x = [1, 2, 3, 4, 5]
x_copy = x.copy()
x[0] = 0
print(x)
print(x_copy)
#创建一个二维列表,命名为y
y = [[1, 2, 3], [4, 5, 6]]
y_copy = y.copy()
y[0][0] = 0
print(y)
print(y_copy)
在pycharm
中运行代码结果如下:
[0, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[[0, 2, 3], [4, 5, 6]]
[[0, 2, 3], [4, 5, 6]]
我们可以发现对于一维列表,改变x的值并不影响x_copy
的值,但对于二维列表改变y的值却也改变了y_copy
的值。为什么呢?因为浅拷贝仅仅是拷贝了外层的对象,而对内层对象只是引用了一下。上面代码使用copy()
方法拷贝二维列表的过程可以表示如下图:
下面来介绍第二种和第三种浅拷贝的方法:
#第二种浅拷贝方法,使用copy包里面的copy函数
import copy
y = [[1, 2, 3], [4, 5, 6]]
y_copy = copy.copy(y)
y[0][0] = 0
print(y)
print(y_copy)
#第三种浅拷贝方法,切片方法
y_copy = y[ : ]
y[0][0] = 0
print(y)
print(y_copy)
运行结果如下,分析与第一种方法一样,这里不再赘述啦。
[[0, 2, 3], [4, 5, 6]]
[[0, 2, 3], [4, 5, 6]]
[[0, 2, 3], [4, 5, 6]]
[[0, 2, 3], [4, 5, 6]]
深拷贝
深拷贝需要使用copy
包里面的deepcopy()
函数:
import copy
y = [[1, 2, 3], [4, 5, 6]]
y_copy = copy.deepcopy(y)
y[0][0] = 0
print(y)
print(y_copy)
运行结果如下:
[[0, 2, 3], [4, 5, 6]]
[[1, 2, 3], [4, 5, 6]]
关于为什么不直接广泛使用深拷贝的问题,up
主给出的理由是因为深拷贝运行速度较慢。
我的问题出在了这里!!!
两个矩阵创建图这样表示:
所以创建B矩阵实质上是对创建出来的一维矩阵[0,0,0]
进行了三次引用,任意一次引用改变一维矩阵的值都会使得三行全改变。
另外介绍一下 is
运算符,用于检验两个变量是不是指向的同一个变量,这里一定要记住:字符串是不能改变的,用is
运算符检验会返回 True
,而列表是可以改变的,会返回 False。还有就是 y = x
命令是将 y
指向 x
指向的对象,是引用关系,不是拷贝。
欢迎大家批评指正,大家一起学习一起进步哇!!!