关于 python 何时自动创建数组副本的文档

Posted

技术标签:

【中文标题】关于 python 何时自动创建数组副本的文档【英文标题】:Documentation on when python automatically creates array copies 【发布时间】:2019-06-30 09:47:12 【问题描述】:

我正在尝试查找有关 python 何时自动复制 numpy 数组的文档。这与数组视图有关。

如果使用简单索引,则赋值会生成数组视图。如果使用高级索引,python 会创建一个副本。我发现其他时候 python 制作数组副本,但无法找到文档。这是一个例子:

a = np.array([1.,2.,3.,4.,5.])
av = a.view()
print(a)
print(av)
a[0] = 100.0
print(av)
a = 0
print(av)

变量 ava 的视图,直到 a 发生足够的变化以至于它不再是视图。这是在哪里记录的?

【问题讨论】:

This Numpy copies & views tutorial 可能是了解正在发生的事情的最佳方式。 av 是原始 aviewa=0 将一个新对象分配给变量a。该更改与numpy 查看/复制问题无关。 谢谢。曾经,av 是变量 a 的一种视图。进行新分配后, av 不再是视图。我正在尝试查找有关 python 如何将视图更改为其他内容以及触发它的文档的文档。它必须是某种逻辑来跟踪 av 是什么视图。然后,当那件事发生变化时,它会改变 av。我正在尝试了解的有趣逻辑。 【参考方案1】:
In [118]: a = np.array([1.,2.,3.,4.,5.]) 
     ...: av = a.view()                                                                                         
In [119]: a                                                                                                     
Out[119]: array([1., 2., 3., 4., 5.])
In [120]: av                                                                                                    
Out[120]: array([1., 2., 3., 4., 5.])

数组是具有dtypeshape 等属性的对象,以及指向数据缓冲区的指针 (base)。我们可以通过以下方式获得这些属性的摘要:

In [121]: a.__array_interface__                                                                                 
Out[121]: 
'data': (69293648, False),
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3

av 是一个新的数组对象,但它具有相同的属性,包括data

In [122]: av.__array_interface__                                                                                
Out[122]: 
'data': (69293648, False),       # same value as for a
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3

copy比较:

In [123]: bv = a.copy()                                                                                         
In [124]: bv.__array_interface__                                                                                
Out[124]: 
'data': (77843920, False),
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3

其他视图可能有不同的值 - 切片具有不同的形状和步幅等。但数据缓冲区将是相同的(__array_interface__['data'] 中显示的实际数字可能会有点不同,指向缓冲区中的不同元素。

更改a 的元素,我们也看到av 中的更改 - 因为共享数据缓冲区:

In [125]: a[0] =12                                                                                              
In [126]: av[0]                                                                                                 
Out[126]: 12.0
In [127]: bv[0]                                                                                                 
Out[127]: 1.0

当您将其他内容分配给 a 时,av 不会改变。

In [128]: a = 10                                                                                                
In [129]: a.__array_interface__                                                                                 
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-129-3b348559b028> in <module>
----> 1 a.__array_interface__

AttributeError: 'int' object has no attribute '__array_interface__'
In [130]: av.__array_interface__                                                                                
Out[130]: 
'data': (69293648, False),
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3

a 是一个整数,而不是一个数组。最初为a 创建的数据缓冲区仍然存在,作为av 的基础。否则原来的a 对象就消失了。

我需要限定最后一条语句 - 由于 ipython 的输出缓冲,该数组对象仍然存在:

In [131]: Out[119].__array_interface__                                                                          
Out[131]: 
'data': (69293648, False),
 'strides': None,
 'descr': [('', '<f8')],
 'typestr': '<f8',
 'shape': (5,),
 'version': 3

In [132]: Out[119][0]                                                                                           
Out[132]: 12.0

我可能应该显示id(a) 等,以澄清

In [133]: id(av)                                                                                                
Out[133]: 139992523130800
In [134]: id(Out[119])                                                                                          
Out[134]: 139992142353312

av 的更改出现在 Out[119] 中,但不在 a 中 - 因为 a 是一个完全不同的对象。

In [144]: av[1] = 100                                                                                           
In [145]: Out[119]                                                                                              
Out[145]: array([ 12., 100.,   3.,   4.,   5.])
In [146]: a                                                                                                     
Out[146]: 10

因此,在某种程度上,您需要了解 Python 变量和对象的本质。例如,b=a 只是创建另一个名称或对同一对象的引用,而不是副本。 b = a.copy() 制作一个副本,但该副本的详细信息取决于对象类。 numpycopy 上添加了一个变体view,可以节省时间和内存。但是你必须了解它的数据存储机制才能理解它。

【讨论】:

【参考方案2】:

来自Advanced Indexing 的 NumPy 文档:

在选择对象,OBJ是非元组序列对象,NDARRAY(数据类型整数或BOOL)或具有至少一个序列对象或NDARRAY(数据类型整数)的元组时触发高级索引布尔)。高级索引有两种类型:整数和布尔值。

高级索引始终返回数据的副本(与返回视图的基本切片相反)。

您可以使用属性base 来检查 ndarray 是另一个副本还是另一个视图。

a = np.arange(0, 10)

b = a.view()
print(b.base is a) # True

c, d = a[2:], a[::2]
print(c.base is a and d.base is a) # True

b = a.copy()
print(b.base is a) # False

b = a[a >= 5]
print(b.base is a) # False

编辑: 我发现有趣的是,您可以为数组“a”创建视图“b”,然后更改原始数组内存地址(使用 resize 方法)。 a 的视图 'base' 属性仍然指向 b,但 b 上的更改不会反映在 a 上:

a = np.arange(0, 10)
b = a.view()
print(np.shares_memory(a, b)) # True
print(b.base is a) # True

a.resize(50, refcheck=False)
print(np.shares_memory(a, b)) # False
print(b.base is a) # True

b[0] = 20
print(a[0] == b[0]) # False

您可以做的另一个技巧是手动更改视图的“数据”属性。如果你这样做,'base' 属性会改变:

a, b = np.arange(0, 10), np.arange(10, 20)
c = a.view()
print(c.base is a) # True

c.data = b.data
print(c.base is a) # False
print(c.base is b) # False
print(c[0]) # 10
print(c.base) # 'Memory at ....'
print(c.base == c.data == b.data) # True

【讨论】:

谢谢。我知道有关索引的文档。我还没有找到关于 python 如何动态地将某些内容从视图更改为非视图的文档。 av 是一种观点。然后它就不是一个视图。这可以按照您的建议使用 base 进行验证。但是,它与切片无关。

以上是关于关于 python 何时自动创建数组副本的文档的主要内容,如果未能解决你的问题,请参考以下文章

数组何时自动初始化为其默认数据类型?

SQL Server 2017 AlwaysOn AG 自动初始化

SQL Server 2017 AlwaysOn AG 自动初始化

学Python,用Python自动创建PDF文档,实现办公自动化

何时获取 numpy 数组的子矩阵返回视图但不返回副本?

Google App Engine 自动缩放如何工作?