考虑以下最小例子。在复制不同嵌套深度的列表元素时,有人可以解释numpy
的明显不一致的逻辑吗?
import numpy as np
L = [[[[1, 1], 2, 3]]]
A1 = np.array(L)
A2 = A1.copy()
A1[0][0][2] = 'xx'
A1[0][0][0][0] = 'yy'
print "\nA1 after changes:\n{}".format(A1)
print "\nA2 only partially changed:\n{}".format(A2)
结果:
A1 after changes:
[[[['yy', 1] 2 'xx']]]
A2 only partially changed:
[[[['yy', 1] 2 3]]]
然后:
>>> print A1[0][0][2] == A2[0][0][2]
False
>>> print A1[0][0][0][0] == A2[0][0][0][0]
True
我很难向自己解释为什么3
没有被替换,但1
更深层次。
A2 = np.array(A, copy=True)
和A2 = np.empty_like(A); np.copyto(A4, A)
的行为与上面的代码相同
A2 = A[:]
与A2 = A
的行为相同:更改后两者相同
import copy; A2 = copy.deepcopy(A)
是我发现创建独立副本的唯一解决方案。
答案 0 :(得分:1)
查看你的数组,并首先了解它的结构:
In [139]: A1
Out[139]: array([[[[1, 1], 2, 3]]], dtype=object)
In [140]: A1.shape
Out[140]: (1, 1, 3)
它是dtype=object
数组;这是元素是对象指针,而不是数字。它也是3d,有3个元素。
In [142]: A1[0,0]
Out[142]: array([[1, 1], 2, 3], dtype=object)
由于它是一个数组,A1[0,0]
优于A1[0][0]
。功能相同,但更清晰。 A1[0,0,:]
甚至更好。无论如何,在这个级别我们仍然有一个形状为(3,)
的数组,即带有3个元素的1d。
In [143]: A1[0,0,0]
Out[143]: [1, 1]
In [144]: A1[0,0,2]
Out[144]: 3
现在我们得到一个列表和数字,A1
的各个元素。该列表是可变的,数字不是。
我们可以将第3个元素(数字)更改为字符串:
In [148]: A1[0,0,2]='xy'
要更改第一个元素的元素,列表,我必须使用混合索引,而不是4级数组索引。
In [149]: A1[0,0,0,0]
...
IndexError: too many indices for array
In [150]: A1[0,0,0][0]='yy'
In [151]: A1
Out[151]: array([[[['yy', 1], 2, 'xy']]], dtype=object)
A1
仍然是一个3d对象数组;我们只是改变了一些元素。 ' xy'变化不同于' yy'更改。一个更改了数组,另一个更改了数组的列表元素。
A2=A1.copy()
创建一个包含A1
元素(数据缓冲区)副本的新数组。因此A2
指向与A1
相同的对象。
' xy'更改了A1
中的指针,但没有更改A2
副本。
' yy'更改修改了A1
指向的列表。 A2
有一个指向同一列表的指针,因此它可以看到更改。
请注意L
,原始嵌套列表会看到相同的更改:
In [152]: L
Out[152]: [[[['yy', 1], 2, 3]]]
A3 = A[:]
生成view
A1
。 A3
与A1
具有相同的数据缓冲区,因此可以看到所有更改。
A4 = A
也会看到相同的更改,但A4
是对A1
的新引用,而不是视图或副本。
之前提出的duplicate answer
涉及列表的引用,副本和深层副本。这与此相关,因为L
是一个列表,A1
是一个对象数组,它在很多方面是一个列表的数组包装器。但是A1
也是numpy数组,它在view
和copy
之间有了额外的区别。
这不是numpy
数组的好用,甚至不是对象dtype版本。这是一个有益的例子,但实际上太混乱了。如果需要对数组执行deepcopy
,则可能使用的数组错误。