Python身份:多重人格障碍,需要代码缩小

时间:2010-01-13 17:45:17

标签: python memory memory-management identity uniqueidentifier

  

可能重复:
  Python “is” operator behaves unexpectedly with integers

我偶然发现了以下Python奇怪的事情:

>>> two = 2
>>> ii = 2

>>> id(two) == id(ii)
True
>>> [id(i) for i in [42,42,42,42]]
[10084276, 10084276, 10084276, 10084276]

>>> help(id)
Help on built-in function id in module __builtin__:

id(...)
    id(object) -> integer

    Return the identity of an object.  This is guaranteed to be unique among
    simultaneously existing objects.  (Hint: it's the object's memory address.)
  1. 每个数字都是唯一的对象吗?
  2. 不同的变量是否具有相同的元素值(例如,2,ii)是同一个对象吗?
  3. Python生成的数字的id是多少?
  4. 在上面的例子中,是指向保持值2的存储单元的两个和二个指针吗?那将是非常奇怪的。
  5. 帮助我解决这种身份危机。

    更多的奇怪之处:<​​/ p>

    >>> a,b=id(0),id(1)
    >>> for i in range(2,1000):
       a,b=b,id(i)
       if abs(a-b) != 12:
        print('%i:%i -> %i' % (i,a,b))
    

    上面的代码检查连续整数的id是否也是连续的,并打印出来 异常:

    77:10083868 -> 10085840
    159:10084868 -> 10086840
    241:10085868 -> 10087840
    257:10087660 -> 11689620
    258:11689620 -> 11689512
    259:11689512 -> 11689692
    260:11689692 -> 11689548
    261:11689548 -> 11689644
    262:11689644 -> 11689572
    263:11689572 -> 11689536
    264:11689536 -> 11689560
    265:11689560 -> 11689596
    266:11689596 -> 11689656
    267:11689656 -> 11689608
    268:11689608 -> 11689500
    331:11688756 -> 13807288
    413:13806316 -> 13814224
    495:13813252 -> 13815224
    577:13814252 -> 13816224
    659:13815252 -> 13817224
    741:13816252 -> 13818224
    823:13817252 -> 13819224
    905:13818252 -> 13820224
    987:13819252 -> 13821224
    

    请注意,从413开始出现一种模式。也许是因为在每个新的记忆页面的开头都有一些巫术会计。

5 个答案:

答案 0 :(得分:9)

-1到255之间的整数(?)以及字符串文字被实习。源中的每个实例实际上代表同一个对象。

在CPython中,id()的结果是PyObject的进程空间中的地址。

答案 1 :(得分:8)

完全允许Python的每个实现在任何程度上进行优化(包括......根本没有;-) 不可变对象(例如数字,元组和字符串)的标识和分配[[可变对象没有这样的自由度,例如列表,字符串和集合]]。

在两个不可变对象引用ab之间,所有实现都必须保证:

  1. id(a) == id(b),AKA a is b,必须始终暗示a == b
  2. 因此a != b必须始终暗示id(a) != id(b) AKA a is not b
  3. 特别注意没有约束,即使对于不可变类型,a == b必须暗示a is b(即id(a) == id(b))。只有None才能保证(因此您始终可以测试if x is None:而不是if x == None:)。

    当前的CPython实现通过“合并”(具有单个分配,因此单个id)来处理特定范围内的小整数,以及内置的不可变类型对象,从而利用这些自由度。文字在给定函数中出现不止一次(例如,如果函数f有四次出现的文字'foobar',它们都将引用函数常量中的单个字符串'foobar'实例与允许的实现相比,节省了一点空间,该实现将存储该常量的四个相同但独立的副本。)

    所有这些实现注意事项对于Python编码器来说都是非常小的兴趣(除非您正在处理Python实现,或者至少是某些与特定实现紧密绑定的内容,例如调试系统)。

答案 2 :(得分:4)

你的第四个问题,“在上面的例子中,两个和两个指针指向一个存储单元格的值为2?这将是非常奇怪的”,这是理解整个事情的关键。

如果你熟悉像C这样的语言,Python“变量”实际上并没有那么相同。一个C变量声明,如:

int j=1;
int k=2;
k += j;

说,“编译器,为我保留两个内存区域,在堆栈上,每个区域都有足够的空间来容纳一个整数,并记住一个为'j'而另一个为'k'。然后用值j填充j '1'和k的值为'2'。“在运行时,代码说“取k的整数内容,添加j的整数内容,并将结果存储回k。”

Python中看似相同的代码:

j = 1
k = 2
k += j

说了一些不同的东西:“Python,查找称为'1'的对象,并创建一个名为'j'的标签指向它。查找称为'2'的对象,并创建一个名为'k的标签'指向它。现在查找对象'k'指向('2'),查找对象'j'指向('1'),并将'k'指向对象,因为执行'在两者上添加“操作”。

反汇编此代码(使用 dis 模块)可以很好地显示:

  2           0 LOAD_CONST               1 (1)
              3 STORE_FAST               0 (j)

  3           6 LOAD_CONST               1 (2)
              9 STORE_FAST               1 (k)

  4          12 LOAD_FAST                1 (k)
             15 LOAD_FAST                0 (j)
             18 INPLACE_ADD
             19 STORE_FAST               1 (k)

所以是的,Python“变量”是指向对象的标签,而不是可以填充数据的容器

其他三个问题都是“Python什么时候从一段代码创建一个新对象,以及何时重用它已经拥有的一个?”的变体。后者被称为“实习”;它发生在较小的整数和字符串上(对Python来说)它们可能是符号名称。

答案 3 :(得分:2)

你应该非常小心这些调查。您正在研究语言实现的内部,并且不能保证这些内容。 id上的帮助是正确的:两个不同对象的数量不同,同一对象的数量相同。作为实现细节,在CPython中它是对象的内存地址。 CPython可能会决定随时更改此细节。

实施相同分配时间的小整数的细节也是一个可能随时改变的细节。

此外,如果您从CPython切换到Jython,PyPy或IronPython,除id()上的文档外,所有投注均已关闭。

答案 4 :(得分:1)

并非每个数字都是唯一的对象,而且某些数字是CPython解释器的优化细节。 不要依赖此行为。就此而言,永远不要使用is来测试相等性。如果您完全确定需要完全相同的对象,请仅使用is