Skip to main content
 首页 » 编程设计

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

2025年05月04日12cmt

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

a = np.arange(3) 
b = a 

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

b[0] = 100 

并打印a、b、它们的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 

所以,a 和 b 看起来一样,而且它们的 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 

在这种情况下,c 和 d 不同,它们的 id 也不同;也符合预期。

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

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

我现在的主要问题是:

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

相关问题:

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

请您参考如下方法:

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

先拿第二个。

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

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

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

我喜欢看数组的.__array_interface__。该字典中的一项是“数据”,它是指向数据缓冲区的指针。相同的指针意味着数据是共享的。但是一个 View 可能从某个地方开始。如果 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 

除 ll[0] 外,其他人均不共享 id。

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 

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

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 

就在这个简短的 session 中,我在 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() 搜索添加一个层。

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

我认为最好的方法是只了解您自己对变量的使用,这样您就可以清楚地识别引用、 View 和副本。在选定的情况下,您可以执行测试,如 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 表示它是从另一个数组创建的,因此可能共享内存。但这只是一个“可能”。

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

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

显然没有对数据的其他引用,但 reshape 后的数组并不“拥有”自己的数据。

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

我必须做

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

保持 OWNDATA 为真。 False OWNDATA 表示在创建新数组对象后立即执行某些操作,但如果重新定义或删除原始引用则它不会更改。它很容易变得过时。