当对数据进行操作时,他们都数据有时被拷贝到一个新的数据,有时则没有。初学者经常会对此感到困惑。以下分三种情况介绍:
不拷贝#
简单的赋值语句将不会拷贝对象和其中的数据。
Python 将可变对象作为引用传递,因此对这类对象,函数调用将不进行拷贝。
import numpy as np
a = np.array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
b = a # 没有创建新对象
print(b is a) # True, a 和 b 是指向同一 ndarray 对象的两个名字
def f(x):
print(id(x))
# 以下两个两行在每次调用时打印相同的值,但具体值则不固定
print(id(a)) # 4379335184
f(a) # 4379335184
视图或浅拷贝#
不同的数组对象可能共享相同的数据。view 方法创建一个新的数据对象,但和原数据共享相同的数据。
import numpy as np
a = np.array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
c = a.view()
print(c is a) # False
print(c.base is a) # True, c 是 a 所拥有的数据的一个视图
print(c.flags.owndata) # False
c = c.reshape((2, 6)) # 改变 c 的形状并不会改变 a 的形状
print(a)
c[0, 4] = 1234 # 改变 c 的数据将同时改变 a 的数据
print(a)
# [[ 0 1 2 3]
# [1234 5 6 7]
# [ 8 9 10 11]]
切分一个数据将会返回它的一个视图。
import numpy as np
a = np.array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
s = a[:, 1:3]
s[:] = 10 # s[:] 是 s 的一个视图。注意 s = 10 和 s[:] = 10 是不同的
print(a)
# [[ 0 10 10 3]
# [ 4 10 10 7]
# [ 8 10 10 11]]
深拷贝#
copy 是对数据及其数据的全面拷贝,被成为深拷贝。
import numpy as np
a = np.array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
d = a.copy() # 创建一个新的具有独立数据的数据
print(d is a) # False
print(d.base is a) # False, d 和 a 不共享任何东西
d[0, 0] = 9999
print(a) # a 还是原来的 a
# [[ 0 1 2 3]
# [ 4 5 6 7]
# [ 8 9 10 11]]
有时在进行切分后,如果原数据已经不再需要,则有必要调用一次 copy。例如,假设 a 是一个巨型的中间结果,而最终结果 b 仅包含 a 的一小部分,那么在通过切分得到 b 后应对其执行一次深拷贝:
import numpy as np
a = np.arange(int(1e8))
b = a[:100].copy()
del a # 释放 a 所占用的内存
要是在以上代码中使用 b = a[:100],那么 a 被 a,即便调用了 del a,其所占用的内存仍不会释放。