如何找到具有相同 ID 的所有变量?

How to find all variables with identical id?

假设我有一个 numpy 数组 a 并像这样创建 b

a = np.arange(3)
b = a

如果我现在更改 b 例如像这样

b[0] = 100

并打印 ab、他们的 id.flags

print a
print a.flags    
print b
print b.flags
print id(a)
print id(b)

我得到

[100   1   2]

  C_CONTIGUOUS : True
  F_CONTIGUOUS : True
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

[100   1   2]

  C_CONTIGUOUS : True
  F_CONTIGUOUS : True
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

139767698376944
139767698376944

因此,ab 看起来一样,而且它们的 id 与预期的一样。

当我现在使用 copy()

做同样的事情时
c = np.arange(3)
d = c.copy()

d[0] = 20

print c
print c.flags
print id(c)

print d
print d.flags
print id(d)

我明白了

[0 1 2]

  C_CONTIGUOUS : True
  F_CONTIGUOUS : True
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

139767698377344

[20  1  2]

  C_CONTIGUOUS : True
  F_CONTIGUOUS : True
  OWNDATA : True
  WRITEABLE : True
  ALIGNED : True
  UPDATEIFCOPY : False

139767698376864

在这种情况下,cd 不同,他们的 id 也是如此;也符合预期。

然而,让我感到困惑的是我从 .flags 获得的输出:在所有情况下,OWNDATA 都设置为 True。当我阅读 documentation 时,我发现:

OWNDATA (O) The array owns the memory it uses or borrows it from another object.

我现在的主要问题是:

查找指向相同 id 的所有变量的最简单方法是什么(在上面的示例中 ab),即检查另一个变量是否具有存在相同的ID?我认为 OWNDATA 会对此有所帮助,但显然没有。

相关问题:

OWNDATA实际用于什么,在这种情况下OWNDATA设置为False

赋值 b=a 不会在原始数组 a 上创建视图,而只是创建对它的引用。换句话说,b 只是 a 的不同名称。变量 ab 都引用 same 数组,该数组拥有其数据,因此设置了 OWNDATA 标志。修改 b 将修改 a.

赋值 b=a.copy() 创建原始数组的副本。也就是说,ab 指的是单独的数组,它们都拥有自己的数据,因此设置了 OWNDATA 标志。修改 b 不会修改 a.

但是,如果您进行赋值 b=a[:],您将创建原始数组的视图并且 b 将不拥有其数据。修改 b 将修改 a.

shares_memory 函数就是您要找的。它按照包装盒上的说明进行操作:检查数组 ab 是否共享内存并因此相互影响。

有 2 个问题 - 如何识别要比较的变量,以及如何比较它们。

先拿第二个。

我的版本 (1.8.2) 没有 np.shares_memory 功能。它确实有一个 np.may_share_memory

https://github.com/numpy/numpy/pull/6166是添加shares_memory的pull request;它的日期是去年八月。所以你必须拥有全新的 numpy 才能使用它。请注意,最终测试可能很困难,并且可能会发出 'TOO HARD' 错误消息。我想,例如,有一些切片共享内存,但很难通过简单地比较缓冲区起点来识别。

https://github.com/numpy/numpy/blob/97c35365beda55c6dead8c50df785eb857f843f0/numpy/core/tests/test_mem_overlap.py 是这些 memory_overlap 函数的单元测试。如果您想了解考虑 2 个已知数组之间所有可能的重叠条件是一项多么艰巨的任务,请阅读它。

我喜欢看数组的.__array_interface__。该字典中的一项是 'data',它是指向数据缓冲区的指针。相同的指针意味着数据是共享的。但是一个视图可能从某个地方开始。如果 shares_memeory 查看此指针,我不会感到惊讶。

相同id表示2个变量引用同一个对象,但不同的数组对象可以共享一个数据缓冲区。

所有这些测试都需要查看特定的参考资料;所以你仍然需要获得某种参考列表。看看locals()?,globals()。未命名的引用,例如数组列表,或一些用户定义的字典呢?

一个例子Ipython 运行:

一些变量和引用:

In [1]: a=np.arange(10)
In [2]: b=a           # reference
In [3]: c=a[:]        # view
In [4]: d=a.copy()    # copy
In [5]: e=a[2:]       # another view
In [6]: ll=[a, a[:], a[3:], a[[1,2,3]]]  # list 

比较id:

In [7]: id(a)
Out[7]: 142453472
In [9]: id(b)
Out[9]: 142453472

None 其他人共享 id,除了 ll[0].

In [10]: np.may_share_memory(a,b)
Out[10]: True
In [11]: np.may_share_memory(a,c)
Out[11]: True
In [12]: np.may_share_memory(a,d)
Out[12]: False
In [13]: np.may_share_memory(a,e)
Out[13]: True
In [14]: np.may_share_memory(a,ll[3])
Out[14]: False

这正是我所期望的;视图共享内存,副本不共享。

In [15]: a.__array_interface__
Out[15]: 
{'version': 3,
 'data': (143173312, False),
 'typestr': '<i4',
 'descr': [('', '<i4')],
 'shape': (10,),
 'strides': None}
In [16]: a.__array_interface__['data']
Out[16]: (143173312, False)
In [17]: b.__array_interface__['data']
Out[17]: (143173312, False)
In [18]: c.__array_interface__['data']
Out[18]: (143173312, False)
In [19]: d.__array_interface__['data']
Out[19]: (151258096, False)            # copy - diff buffer
In [20]: e.__array_interface__['data'] 
Out[20]: (143173320, False)            # differs by 8 bytes
In [21]: ll[1].__array_interface__['data']
Out[21]: (143173312, False)            # same point

就在这个简短的会话中,我在 locals() 中有 76 个项目。但我可以搜索它以匹配 id 与:

In [26]: [(k,v) for k,v in locals().items() if id(v)==id(a)]
Out[26]: 
[('a', array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])),
 ('b', array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))]

其他测试也一样。

我可以用同样的方法搜索ll:

In [28]: [n for n,l in enumerate(ll) if id(l)==id(a)]
Out[28]: [0]

而且我可以通过测试某个项目是列表还是字典并在其中进行搜索来为 locals() 搜索添加一层。

因此,即使我们确定了测试方法,搜索所有可能的参考资料也并非易事。

我认为最好的方法是只了解您自己对变量的使用,这样您就可以清楚地识别引用、视图和副本。在选定的情况下,您可以执行 may_share_memory 之类的测试或比较数据缓冲区。但没有一种廉价的、确定性的测试。如有疑问,复制一份比冒险重写要便宜。在我多年的 numpy 使用中,我从未觉得需要对这个问题给出明确的答案。


我觉得 OWNDATA 标志不是很有用。考虑以上变量

In [35]: a.flags['OWNDATA']
Out[35]: True
In [36]: b.flags['OWNDATA']   # ref
Out[36]: True
In [37]: c.flags['OWNDATA']   # view
Out[37]: False
In [38]: d.flags['OWNDATA']   # copy
Out[38]: True
In [39]: e.flags['OWNDATA']   # view
Out[39]: False

虽然我可以预测这些简单情况下的 OWNDATA 值,但它的值并没有说明共享内存或共享 ID。 False 表明它是从另一个数组创建的,因此可能共享内存。但这只是一个 'may'.

我经常通过重塑范围来创建示例数组。

In [40]: np.arange(3).flags['OWNDATA']
Out[40]: True
In [41]: np.arange(4).reshape(2,2).flags['OWNDATA']
Out[41]: False

显然没有对数据的其他引用,但重组后的数组没有 'own' 它自己的数据。

也会发生同样的情况
temp = np.arange(4); temp = temp.reshape(2,2)

我必须做

temp = np.arange(4); temp.shape = (2,2)

保持 OWNDATA 正确。 False OWNDATA 表示在创建新数组对象后立即发生,但如果重新定义或删除原始引用,它不会改变。它很容易过时。