numpy@浅拷贝和深拷贝

文章介绍了Numpy中数组操作的三种情况:不复制(NoCopy)、浅复制(View/ShallowCopy)和深复制(DeepCopy)。浅复制创建了一个指向原始数据的新引用,而深复制则创建了数据的完整副本。文章强调了深复制在节省内存方面的应用,并通过示例展示了如何影响和修改原数组及其副本。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Copies and Views

When operating and manipulating arrays, their data is sometimes copied into a new array and sometimes not. This is often a source of confusion for beginners. There are three cases:

No Copy at All

Simple assignments make no copy of objects or their data.

>>> a = np.array([[ 0,  1,  2,  3],
...               [ 4,  5,  6,  7],
...               [ 8,  9, 10, 11]])
>>> b = a            # no new object is created
>>> b is a           # a and b are two names for the same ndarray object
True

Python passes mutable objects as references, so function calls make no copy.

>>> def f(x):
...     print(id(x))
...
>>> id(a)  # id is a unique identifier of an object 
148293216  # may vary
>>> f(a)   
148293216  # may vary

View or Shallow Copy

Different array objects can share the same data. The view method creates a new array object that looks at the same data.

>>> c = a.view()
>>> c is a
False
>>> c.base is a            # c is a view of the data owned by a
True
>>> c.flags.owndata
False
>>>
>>> c = c.reshape((2, 6))  # a's shape doesn't change
>>> a.shape
(3, 4)
>>> c[0, 4] = 1234         # a's data changes
>>> a
array([[   0,    1,    2,    3],
       [1234,    5,    6,    7],
       [   8,    9,   10,   11]])

Slicing an array returns a view of it:

>>> s = a[:, 1:3]
>>> s[:] = 10  # s[:] is a view of s. Note the difference between s = 10 and s[:] = 10
>>> a
array([[   0,   10,   10,    3],
       [1234,   10,   10,    7],
       [   8,   10,   10,   11]])

Deep Copy

  • The copy method makes a complete copy of the array and its data.

  • >>> d = a.copy()  # a new array object with new data is created
    >>> d is a
    False
    >>> d.base is a  # d doesn't share anything with a
    False
    >>> d[0, 0] = 9999
    >>> a
    array([[   0,   10,   10,    3],
           [1234,   10,   10,    7],
           [   8,   10,   10,   11]])
    

节约内存的技巧

  • Sometimes copy should be called after slicing if the original array is not required anymore. For example, suppose a is a huge intermediate result and the final result b only contains a small fraction of a, a deep copy should be made when constructing b with slicing:

  • >>> a = np.arange(int(1e8))
    >>> b = a[:100].copy()
    >>> del a  # the memory of ``a`` can be released.
    
  • If b = a[:100] is used instead, a is referenced by b and will persist in memory even if del a is executed.

补充试验

  • 以下试验在jupyter notebook 下进行
import numpy as np


a = np.array([[ 0,  1,  2,  3],
               [ 4,  5,  6,  7],
               [ 8,  9, 10, 11]])
b=a
print(a==b)
print(a is b)
print(id(a)==id(b))
    [[ True  True  True  True]
     [ True  True  True  True]
     [ True  True  True  True]]
    True
    True
  • ​ 浅拷贝,得到源对象a的视图c
c=a.view()

c,type(c)
    (array([[ 0,  1,  2,  3],
            [ 4,  5,  6,  7],
            [ 8,  9, 10, 11]]),
     numpy.ndarray)
  • a,c的类型都是ndarray,他们又一定的联系,但是他们并不等价
print(c is a,id(c),id(a),c.base is a)
False 2296426462928 2296415339600 True

  • 检查cc.base的类型(都是ndarray)
type(c.base),type(c)
(numpy.ndarray, numpy.ndarray)
  • 检查一个ndarray对象是否为其他某个对象的视图
c.flags.owndata,a.flags.owndata
#说明c的数据是来自于其他对象(比如是其他对象调用view生成的),而a的数据是来自自己的
    (False, True)
print(id(c))
c=c.reshape(2,6)
print(id(c))
    2296426462928
    2296415340656
  • ​ 修改视图c的形状,并不会影响源对象a的形状
c,a
    (array([[ 0,  1,  2,  3,  4,  5],
            [ 6,  7,  8,  9, 10, 11]]),
     array([[ 0,  1,  2,  3],
            [ 4,  5,  6,  7],
            [ 8,  9, 10, 11]]))
  • 试验通过修改视图c的某个元素来修改源对象a的元素
c[0,4]=123
#如果对一个对象a的view(记为c)作修改,那么对象a会受到c的变换的影响(因为c.base=a)
#然而ndarray对象的形状(shape)和数据(data)又相对独立,修改c的某个元素,a中对应的元素也被修改,但是修改c的形状,a的形状却不会改变
#元素的对应关系可以分别flatten后对应起来
c,a
    (array([[  0,  10,  10,   3, 123,  10],
            [ 10,   7,   8,  10,  10,  11]]),
     array([[  0,  10,  10,   3],
            [123,  10,  10,   7],
            [  8,  10,  10,  11]]))
  • 改变完视图c的shape,再次检查c.base是否依然等价于a
c.base is a
    True

Slicing an array returns a view of it:

s = a[:, 1:3]
s[:] = 10  # s[:] is a view of s. Note the difference between s = 10 and s[:] = 10
a,s

    (array([[  0,  10,  10,   3],
            [123,  10,  10,   7],
            [  8,  10,  10,  11]]),
     array([[10, 10],
            [10, 10],
            [10, 10]]))
t=a[:,1:3]
t
    array([[10, 10],
           [10, 10],
           [10, 10]])
t=100
t

​ 100

x=np.arange(12).reshape(4,3)
x,id(x)

​ (array([[ 0, 1, 2],
​ [ 3, 4, 5],
​ [ 6, 7, 8],
​ [ 9, 10, 11]]),
​ 2296338927792)

x=99
x,id(x)

​ (99, 2296148266288)

# a="str1"
# b=a
# print(b is a)

​ True

Deep Copy

d = a.copy()  # a new array object with new data is created
a,d

​ (array([[ 0, 10, 10, 3],
​ [123, 10, 10, 7],
​ [ 8, 10, 10, 11]]),
​ array([[ 0, 10, 10, 3],
​ [123, 10, 10, 7],
​ [ 8, 10, 10, 11]]))

d is a,id(d),id(a)

​ (False, 2296338931824, 2296415339600)

d.base,a.base#可以发现a的深拷贝d,其d.base,与a本身的a.base是None,因为他们的地位是等同的,除了具有一样的元素,在内存上完全独立
# d doesn't share anything with a

​ (None, None)

c.base#而c是a的浅拷贝(view),所以具有非None的base(c.base 就是 a)

​ array([[ 0, 10, 10, 3],
​ [123, 10, 10, 7],
​ [ 8, 10, 10, 11]])

d.base==a,

​ (array([[False, False, False, False],
​ [False, False, False, False],
​ [False, False, False, False]]),)

d[1,1]=9999
d,a

​ (array([[ 0, 10, 10, 3],
​ [ 123, 9999, 10, 7],
​ [ 8, 10, 10, 11]]),
​ array([[ 0, 10, 10, 3],
​ [123, 10, 10, 7],
​ [ 8, 10, 10, 11]]))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cxxu1375

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值