Python,变量存储在内存中

时间:2017-04-08 13:51:46

标签: python

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])不同?

4 个答案:

答案 0 :(得分:2)

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

  1. 解析,并将其编译为字节码的代码对象,然后
  2. 执行字节码。
  3. 可以通过dis module

    检查编译结果
    >>> 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
    

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

C数组只是一个连续内存块,因此根据定义,其第一个(第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}}对象的数字1.因此int是该对象的“指针”。

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

由于没有必要让两个对象表示相同的数字1,因此id(x[0])在Python解释器运行期间始终是相同的。举例说明:

id(1)

CPython实际上为一些最常用的小数字(see comments here)预分配对象。对于较大的数字,情况并非如此,can lead to two 'copies' of a larger number having different id() values

答案 3 :(得分:1)

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

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

至于你的情况,即使两者的值都相同,也不能保证[0]和[1]会给出相同的id。它取决于在python程序生命周期中创建文字/变量的顺序/时间顺序,并由python内部处理。

案例1:

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

案例2(请注意,在案例结尾处,[0]和[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
>>>