python中列表和numpy数组的地址和一些奇怪的东西

python中列表和numpy数组的地址和一些奇怪的东西,python,pointers,memory-address,Python,Pointers,Memory Address,如果我能在python中看到列表、数组和字符串的内存地址,我会很好奇,因为我发现了一些有趣而奇怪的东西。有人能解释一下发生了什么事吗 >>> l = [1,1,2,2,3,3] >>> for i in range(6): ... adr = str( id(l[i]) ) ... print(f'{l[i]}: {adr[-6:]}') ... 1: 422400 1: 422400 2: 422432 2: 422432 3: 42246

如果我能在python中看到列表、数组和字符串的内存地址,我会很好奇,因为我发现了一些有趣而奇怪的东西。有人能解释一下发生了什么事吗

>>> l = [1,1,2,2,3,3]
>>> for i in range(6):
...     adr = str( id(l[i]) )
...     print(f'{l[i]}: {adr[-6:]}')
... 
1: 422400
1: 422400
2: 422432
2: 422432
3: 422464
3: 422464
>>> 
您可以清楚地看到,根据id文档,具有相同值的元素将位于相同的内存地址中:

CPython实现细节:这是内存中对象的地址

字符串也会发生这种情况 第二件奇怪的事情发生在numpy数组上,我不知道列表和字符串是否会发生这种情况

>>> arr
array([1, 2, 3, 4])
>>> id(arr)
140318415946496
>>> id(arr[0])
140318415101680
>>> id(arr[1])
140318415101680
>>> id(arr)
140318415946496
>>> id(arr[0])
140318415101904
>>> id(arr[1])
140318415101904
>>> id(arr)
140318415946496
>>> id(arr[0])
140318415101680
>>> id(arr[1])
140318415101680
>>> 
每当我为arr的地址调用id时,arr[0]的地址都会更改。

在Python3.8.0上运行它会变得更好,如果多次调用
arr[0]
,每次都会得到不同的结果。原因如下:虽然常规列表包含对(Python)对象的引用,但numpy数组在内部将数据存储为C数组。在这种情况下,无论何时尝试查询项的标识,都必须首先创建一个新的Python对象

一般来说,整数的标识没有意义,如中所述:例如

a,b=256256
#`is`=比较对象标识的运算符
a是b#对的
a、 b=257257
a是b#False
在这种情况下,
is
/
id
的行为毫无意义,因为它取决于实现细节、编译器优化(在交互式解释器和脚本中运行代码的结果可能不同)等。下面讨论与numpy相关的情况:

总之,这些东西很有趣,但对程序员没有帮助,因为标识和
is
操作符不应该这样使用。
id
/
的两个相关案例是

  • 测试某个内容是否
    ,或
  • 检查变异
    x
    是否也会变异
    y
    ,因为两者引用相同的对象

()

奇怪的是列表,而不是数组。无论如何,您看到的小整数和(可能)小字符串的行为是由于内部优化而产生的实现细节。你应该对这种行为感到惊讶。Numpy会在索引时创建一个新对象,因为Numpy.ndarray对象包含基本的数字数组,所以对于这个a和b示例,对具有相同id值的元素的解释是相同的?您是指第一个示例,列表
l=[1,1,2,2,3,3]
?是的,因为当前的CPython实现为
-5
256
()之间的所有整数保留一个整数对象数组。从
257
开始,将生成新对象。关于Numpy:Numpy数组在内部存储为C数组。数组中的每个条目都只是一个数字。每当访问数组项时,Numpy都会根据一个数字创建一个Python对象。原因是Numpy针对阵列上的高效存储和矢量化操作进行了优化,而不是针对单个元素的访问。