赋值是将一个对象的地址赋值给一个变量,让变量指向该地址( 旧瓶装旧酒 )。
修改不可变对象(str、tuple)需要开辟新的空间
修改可变对象(list等)不需要开辟新的空间
接着,用一个例子来学习浅拷贝:
import numpy as np
a = np.array([-45,-66,2,25,99,-33])
b = a
b is a
>>>True
id(a)
>>>2434667393392
id(b)#id(a)、id(b)结果相同
>>>2434667393392
a == b
>>>array([ True, True, True, True, True, True])
a[0] = 6
a
>>>array([ 6, -66, 2, 25, 99, -33])
b
>>>array([ 6, -66, 2, 25, 99, -33])
从上述代码以及运行结果可以看出,共存内存地址的两个变量,当其中一个变量的值改变时,另一个变量也随之改变,此时,变量间的“拷贝”是浅拷贝。
浅拷贝只是将容器中元素的地址复制了一份,是在另一块地址中创建一个新的变量或容器,但是容器里面的元素的地址均是源对象的元素和地址的拷贝也就是所谓的“新瓶装旧酒”。
e = np.array([-45,-66,2,25,99,-33])
f = e.view()#f与e“共享视图”
id(e)
>>>2434688036912
id(f)
>>>2434688037712
f.shape=(2,3)
f
>>>array([[-45, -66, 2],
[ 25, 99, -33]])
e[0] = 0
e
>>>array([ 0, -66, 2, 25, 99, -33])
f
>>>array([[ 0, -66, 2],
[ 25, 99, -33]])
f[1,1]=88
f
>>>array([[ 0, -66, 2],
[ 25, 88, -33]])
e
>>>array([ 0, -66, 2, 25, 88, -33])
共享“视图”(view)的两个变量,当其中一个变量的值改变时,另一个变量的值也随之改变。此时,变量间的“拷贝”也是“浅拷贝”
深拷贝是在另一块地址中创建一个新的变量或容器,同时容器内的元素的地址也是新开辟的,仅仅是值相同而已,是完全的副本。也就是“新瓶装新酒 ”。
举例说明:
c = np.array([-45,-66,2,25,99,-33])
d = np.array([-45,-66,2,25,99,-33])
id(c)
>>>2434687986224
id(d)
>>>2434687986784
c == d
>>>array([ True, True, True, True, True, True])
c[1] = 666
c
>>>array([-45, 666, 2, 25, 99, -33])
d
>>>array([-45, -66, 2, 25, 99, -33])
因此,深拷贝是完全内存拷贝,它将多维列表中的内容递归的复制一遍,重新存储到一块新的内存空间。因此在深拷贝不会出现内存公用的情况。
e
>>>array([ 0, -66, 2, 25, 88, -33])
g = np.copy(e)
g
>>>array([ 0, -66, 2, 25, 88, -33])
e[0] = 8
e
>>>array([ 8, -66, 2, 25, 88, -33])
g
>>>array([ 0, -66, 2, 25, 88, -33])
id(e)
>>>2434688036912
id(g)
>>>2434688007184
通过“深拷贝”得到的变量互不干扰,其中一个变量的值改变时,不影响其他变量的值。
x = np.arange(10,1,-1)
x
>>>array([10, 9, 8, 7, 6, 5, 4, 3, 2])
x[[3,3,1,8]]
>>>array([7, 7, 9, 2])
此处,获取x中的下标为3,3,1,8的4个元素,组成一个新的数组。
b = x[np.array([3,3,-3,8])]
b
>>>array([7, 7, 4, 2])
b[2]=100
b
>>>array([ 7, 7, 100, 2])
x
>>>array([10, 9, 8, 7, 6, 5, 4, 3, 2])
x[[3,5,1]] = -1,-2,-3
x
>>>array([10, -3, 8, -1, 6, -2, 4, 3, 2])
b的值改变,而x的值没有改变的原因是由于b和x不共享数据空间,因此x中的值并没有改变。
最后一行中,整数序列下标也可以用来修改元素的值。
x = np.arange(5,0,-1)
x
>>>array([5, 4, 3, 2, 1])
x[np.array([True,False,True,False,False])]
>>>array([5, 3])
布尔数组中下标为0,2的元素为True,以此获取x中下标为0,2的元素
x[np.array([True,False,True,True,False])] = -1,-2,-3
x#①
>>>array([-1, 4, -2, -3, 1])
x = np.random.rand(10)#②
x
>>>array([0.60291436, 0.66126483, 0.52988857, 0.98299845, 0.60645453,
0.29877549, 0.29959646, 0.9257346 , 0.01818018, 0.14690692])
x[x > 0.5]
>>>array([0.60291436, 0.66126483, 0.52988857, 0.98299845, 0.60645453,
0.9257346 ])
①处可以知道,布尔数组下标也可以用来修改元素
②处可以产生一个长度为10,元素值为0-1的随机数的数组
- 浅拷贝可以使用列表自带的copy()函数,或者使用copy模块的copy()函数。深拷贝只能使用copy模块的deepcopy(),所以使用前要导入:from copy import deepcopy
- 如果拷贝的对象里的元素只有值,没有引用,那浅拷贝和深拷贝没有差别,都会将原有对象复制一份,产生一个新对象,对新对象里的值进行修改不会影响原有对象,新对象和原对象完全分离开。
- 如果拷贝的对象里的元素包含引用(像一个列表里储存着另一个列表,存的就是另一个列表的引用),那浅拷贝和深拷贝是不同的,浅拷贝虽然将原有对象复制一份,但是依然保存的是引用,所以对新对象里的引用里的值进行修改,依然会改变原对象里的列表的值,新对象和原对象完全分离开并没有完全分离开。而深拷贝则不同,它会将原对象里的引用也新创建一个,即新建一个列表,然后放的是新列表的引用,这样就可以将新对象和原对象完全分离开。