繁体   English   中英

Python,变量存储在内存中

[英]Python , variable store in memory

a=[1234,1234] #list

a      
[1234, 1234] 

id(a[0])      
38032480

id(a[1])      
38032480

b=1234 #b is a variable of integer type

id(b)      
38032384

为什么id(b)与python中的id(a [0])和id(a [1])不同?

当CPython REPL执行一行时,它将:

  1. 解析,并将其编译为字节码的代码对象,然后
  2. 执行字节码。

可以通过dis模块检查编译结果:

>>> dis.dis('a = [1234, 1234, 5678, 90123, 5678, 4321]')
  1           0 LOAD_CONST               0 (1234)
              2 LOAD_CONST               0 (1234)
              4 LOAD_CONST               1 (5678)
              6 LOAD_CONST               2 (90123)
              8 LOAD_CONST               1 (5678)
             10 LOAD_CONST               3 (4321)
             12 BUILD_LIST               6
             14 STORE_NAME               0 (a)
             16 LOAD_CONST               4 (None)
             18 RETURN_VALUE

请注意,所有1234都加载了“ LOAD_CONST 0 ”,所有5678都加载了“ LOAD_CONST 1 ”。 这些引用与代码对象关联的常量表。 在这里,表格是(1234, 5678, 90123, 4321, None)

编译器知道代码对象中所有1234的副本都是相同的 ,因此只会为所有对象分配一个对象。

因此,正如OP所观察到的, a[0]a[1]确实确实指向同一对象:该行代码的代码对象常量表中的同一常量。

当执行b = 1234 ,它将独立于上一行再次被编译和执行,因此将分配一个不同的对象。

(您可以阅读http://akaptur.com/blog/categories/python-internals/以获取有关如何解释代码对象的简要介绍)


在REPL之外,当您执行*.py文件时,每个函数都会编译成单独的代码对象,因此在运行时:

a = [1234, 1234]
b = 1234
print(id(a[0]), id(a[1]))
print(id(b))

a = (lambda: [1234, 1234])()
b = (lambda: 1234)()
print(id(a[0]), id(a[1]))
print(id(b))

我们可能会看到类似:

4415536880 4415536880
4415536880
4415536912 4415536912
4415537104
  • 前三个数字共享相同的地址4415536​​880,它们属于“ __main__”代码对象的常量
  • 然后, a[0]a[1]具有第一个lambda的地址4415536​​912。
  • b具有第二个lambda的地址4415537104。

另请注意,此结果仅对CPython有效。 其他实现在分配常量方面有不同的策略。 例如,在PyPy中运行上面的代码将给出:

19745 19745
19745
19745 19745
19745

没有规则或保证说明id(a [0])应该等于id(a [1]),因此问题本身是没有根据的。 您应该问的问题是为什么id(a[0])id(a[1])实际上相同。
如果在a.append(1234)之后加上id(a[2]) ,则可能会或可能不会获得相同的ID。 正如@hiro protagonist指出的那样,这些只是内部优化,您不应依赖这些优化。

Python列表与C数组非常不同。

AC阵列只是一块连续的内存,因此,根据定义,其第一个(第0个)元素的地址就是该阵列本身的地址。 C语言中的数组访问只是指针算法, []符号只是该指针算法上语法糖的薄壳。 表达式int x[]只是int * x另一种形式。

为了便于说明,我们假设在Python中, id(x)是“ X的内存地址”,就像*x在C中一样。(并非对所有Python实现都是如此,甚至不能保证在CPython。这只是一个唯一的数字。)

在C中,一个int只是与体系结构有关的字节数,因此,对于int x = 1 ,表达式*x指向这些字节。 Python中的所有内容都是一个对象, 包括数字 这就是为什么id(1)引用一个int类型的对象描述1 您可以调用其方法: (1).__str__()将返回字符串'1'

因此,当x = [1, 2, 3]id(x)是具有三个元素的list对象的“指针”。 list对象本身非常复杂。 但是x[0]不是组成整数1的字节; 它在内部是对数字1的int对象的引用 。因此, id(x[0])是该对象的“指针”。

用C术语来说,数组的元素可以看作是指向存储在其中的对象的指针,而不是对象本身的指针。

由于没有必要让两个对象代表相同的数字1,因此在Python解释器运行期间id(1)始终相同。 插图:

x = [1, 2, 3]
y = [1, 100, 1000]

assert id(x) != id(y)  # obviously
assert id(x[0]) == id(y[0]) == id(1) # yes, the same int object

CPython实际上为一些最常用的小数字预分配了对象( 请参阅此处的注释 )。 对于较大的数字,事实并非如此,这可能导致两个较大的“副本”具有不同的id()

您必须注意:id()实际上给出变量或文字值的id。 对于程序中使用的每个文字/值(即使在id()本身内),id()都会返回(试图返回)程序生命周期内文字/变量的唯一标识符。 可用于:

  • 用户:检查两个对象/变量是否与以下内容相同:a为b
  • Python:优化内存,即避免内存中相同内容的不必要重复

对于您的情况,即使a [0]和a [1]的值可以相同,也无法保证。 它取决于在python程序生命周期中由内部处理的文字/变量的创建顺序/时间顺序。

情况1:

Type "help", "copyright", "credits" or "license" for more information.
>>> a=[1234,1234] 
>>> id(a[0])
52687424
>>> id(a[1])
52687424

情况2(请注意,在情况末尾,a [0]和a [1]的值相同,但id不同):

Type "help", "copyright", "credits" or "license" for more information.
>>> a=[1,1234]
>>> id(1)
1776174736
>>> id(1234)
14611088
>>> id(a[0])
1776174736
>>> id(a[1])
14611008
>>> a[0]=1234
>>> id(1234)
14611104
>>> id(a[0])
14611152
>>> id(a[1])
14611008
>>>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM