【问题标题】:Python variable allocation and `id` keyword [duplicate]Python变量分配和`id`关键字[重复]
【发布时间】:2012-12-08 04:26:34
【问题描述】:

可能重复:
Python “is” operator behaves unexpectedly with integers
Why (0-6) is -6 = False?

所以,在使用id(python 2.6.5)时,我注意到以下(shell 会话):

>>> a = 1
>>> id(a)
140524904
>>> b = 1
>>> id(b)
140524904

当然,只要我修改其中一个变量,它就会被分配到一个新的内存地址,即

>>> b += 1
>>> id(b)
140524892

最初将具有相同值的两个变量分配到相同的内存位置是正常的行为,还是只是 CPython 的优化?

附:我花了一点时间浏览parser 中的代码,但找不到变量分配的位置和方式。

【问题讨论】:

标签: python parsing variables


【解决方案1】:

ab 都引用内存中的同一对象 (1),ID 为 140524904。完成b += 1 后,您就有了2,它位于其他位置。

【讨论】:

  • a=1000 和 b=1000 怎么样?
【解决方案2】:
  1. 在 python 中,所有变量都是指向某些对象的指针。偶数。
  2. 数字是不可变对象。因此,CPython 不需要创建具有相同值的新对象。
  3. 这并不意味着 CPython 将始终使用相同的对象。
  4. 在您的第一个示例中,变量 ab 指向同一个对象。
  5. 当您制作 b += 1 时,您“创建”了新对象 2

【讨论】:

  • +1 我知道元组和字符串是不可变的,但我从未想过数字也是如此(或者我可能错过了那个)。
【解决方案3】:

这里的术语“变量”必须精确:一方面是对象,另一方面是绑定到对象的名称。

如果您使用a = b = 1,则ab 都绑定到表示1 的同一个对象。

如果你这样做a = 1; b = 1,我认为它是相同的 CPython 细节。一般来说,一个实现可以选择有两个对象,它们都代表1,并在这里都使用它们。但是因为这样会浪费内存,所以一般不这样做。

【讨论】:

    【解决方案4】:

    作为mentioned by glglgl,这是CPython 的一个实现细节。如果您查看 CPython 源代码(例如版本 3.3.0)中的 Objects/longobject.c,您会找到正在发生的事情的答案:

    #if NSMALLNEGINTS + NSMALLPOSINTS > 0
    /* Small integers are preallocated in this array so that they
       can be shared.
       The integers that are preallocated are those in the range
       -NSMALLNEGINTS (inclusive) to NSMALLPOSINTS (not inclusive).
    */
    static PyLongObject small_ints[NSMALLNEGINTS + NSMALLPOSINTS];
    

    这就解释了为什么在a = 1; b = 1 之后,a is b 将是True,即使你说a += 2; b +=2; a -= 2; b -= 2。每当一个数字被计算为具有适合该数组的值时,就会从该数组中简单地选取生成的对象,从而节省一点内存。

    您可以使用如下函数计算出small_ints 数组的边界:

    def binary_search(predicate, lo, hi):
        while lo + 1 < hi:
            mid = (lo + hi) / 2
            if predicate(mid):
                lo = mid
            else:
                hi = mid
        return lo
    
    def is_small_int(n):
        p = n + 1
        q = n + 1
        return (p - 1) is (q - 1)
    
    def min_neg_small_int():
        p, q = -1, -1
        if p is not q:
            return 0
        while p is q:
            p += p
            q += q
        return binary_search(is_small_int, p / 2, p) - 1
    
    def max_pos_small_int():
        p, q = 1, 1
        if p is not q:
            return 0
        while p is q:
            p += p
            q += q
        return binary_search(is_small_int, p / 2, p)
    
    def small_int_bounds():
        return (min_neg_small_int(), max_pos_small_int())
    

    对于我的构建(Python 2.7、64 位 Windows 构建),small_int_bounds() == (-5, 256)。这意味着-5256(含)之间的数字通过Objects/longobject.c 中的small_ints 数组共享。

    -edit- 我看到elssar noted 有一个similar answer about interning of some literalsin the documentation for PyInt_FromLong 也提到了这个事实,this answer 也提到了这一点。

    【讨论】:

    • 非常感谢您的彻底回答(以及 python 源代码中的指针——这需要一段时间才能发现)!并感谢所有其他有用的答案和 cmets!
    • 我花了一段时间才找到它的唯一原因是因为我正在寻找名称中带有 int 的东西。但是一旦你找到这个源文件,它几乎就是其中的第一件事。那可能是因为,我引用,“/* XXX The functional organization of this file is terrible */”;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-15
    • 2017-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-04
    相关资源
    最近更新 更多