Python中的赋值、视图、副本与拷贝

Zielorem

数组对象在创建后会存在唯一标识数组内容的通用标识符(即id值)与其数组内容在内存中的存储地址,通过不同方式创建基于原数组的新数组会在这两个属性的基础上产生不同的结果。

赋值与无拷贝

将原数组直接赋值给新数组后,新数组同原数组的标识符与存储地址一致。因此,新数组可以粗略理解为完全通过原数组访问。因此在直接赋值过程中并未创建拷贝。

1
2
3
4
5
6
7
8
9
10
11
a = np.array([[1, 2, 3], [3, 4, 5]])
b = a # a直接赋值给b
print(id(a) == id(b)) # True,标识符一致
print(a.data == b.data)
# True,在物理内存中指向的存储地址一致
print(a is b) # True,a与b实际上是同一个数组

# 任一数组的变化会直接同步在其他数组上
print(a.shape)
b.shape = (3, 2)
print(a.shape == b.shape) # True

视图与浅拷贝

在numpy中对原数组(ndarray)进行切片操作或通过view()方法创建视图后,新数组同原数组的存储地址一致但标识符不一致。介于numpy在进行切片操作时直接操作了原数组,产生的新数组虽变更了标识符,但实际将原数组所在内存地址引用并赋值给了新数组,因此新数组仍基于原数组的存储地址进行内容访问。因此,修改新数组中的数据内容会直接影响并同步给原数组,但诸如变形之类的操作则不会。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
a = np.array([[1, 2, 3], [3, 4, 5]])
b = a[:] # 创建基于a的切片
c = a.view() # 创建基于a的视图
print(id(a) == id(b) == id(c)) # False,标识符不一致
print(a.data == b.data == c.data)
# True,在物理内存中指向的存储地址一致
print(a is b is c) # False,a、b、c不是同一个数组

# 对数组进行赋值操作会影响其他数组
a[0, 2] = 4
print(b[0, 2] == c[0, 2] == 4) # True

# 对数组进行变形操作不会影响其他数组
print(a.shape)
b.shape = (3, 2)
print(a.shape == b.shape) # False

副本与深拷贝

对python序列(tuple)或列表(list)等对象进行切片操作、通过deepCopy()方法创建深拷贝或在numpy中通过copy()方法创建副本后,新数组同原数组的存储地址一致与标识符均不一致,两者之间相互独立。介于python进行切片操作时首先调用deepCopy()方法复制了原数组的深拷贝,然后再在该拷贝上进行操作,因此两者互不相关。

1
2
3
4
5
6
7
8
9
a = [1, 2, 3, 4, 5]
b = a[:] # 创建基于a的切片
c = a.copy() # 创建基于a的副本
print(id(a) == id(b) == id(c)) # False,标识符不一致
print(a is b is c) # False,a、b、c不是同一个数组

# 对数组进行操作不会影响其他数组
a[2] = 4
print(b[2] == 4 or c[2] == 4) # False

python列表(list)对象进行append()操作时,实际进行了直接拷贝。

1
2
3
4
5
list1 = [1, 2]
num = 3
list1.append(num)
print(list1)
print(id(num) == id(list1[2])) # True

因此,若要避免因修改变量值导致的数组数据变化,可通过深拷贝解决。

1
2
3
4
5
list1 = [1, 2]
num = 3
list1.append(copy.deepcopy(num))
print(list1)
print(id(num) == id(list1[2])) # False

值得注意的是,numpy的ndarray对象进行append()操作需要将结果赋给其他的对象,因此经扩展后生成的新数组与原数组无关,所扩展的值与新数组中的对应扩展元素也无关。

1
2
3
4
list1 = np.array([1, 2])
num = 3
list2 = np.append(list1, num)
print(id(num) == id(list2[2])) # False

总结

操作 拷贝类型 标识符 内存地址
直接赋值 一致 一致
numpy切片 浅拷贝 不同 一致
python切片 深拷贝 不同 不同
view()方法 浅拷贝 不同 一致
copy()方法 深拷贝 不同 不同
  • Title: Python中的赋值、视图、副本与拷贝
  • Author: Zielorem
  • Created at : 2022-10-19 14:47:15
  • Updated at : 2023-07-14 01:06:24
  • Link: https://zielorem.github.io/2022/10/19/view-and-copy/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments
On this page
Python中的赋值、视图、副本与拷贝