为什么在快速调用时,Python类的id不是唯一的?

use*_*555 32 python class python-3.x

我在python中做了一些事情(使用python 3.3.3),我遇到了令我困惑的事情,因为我的理解课每次被调用时都会得到一个新的id.

让我们说你在一些.py文件中有这个:

class someClass: pass

print(someClass())
print(someClass())
Run Code Online (Sandbox Code Playgroud)

以上返回相同的id,这让我感到困惑,因为我正在调用它所以它不应该是相同的,对吧?当连续两次调用同一个类时,python是如何工作的?当我等待几秒钟时,它给出了不同的id,但是如果我像上面的例子一样,它似乎不会那样工作,这让我感到困惑.

>>> print(someClass());print(someClass())
<__main__.someClass object at 0x0000000002D96F98>
<__main__.someClass object at 0x0000000002D96F98>
Run Code Online (Sandbox Code Playgroud)

它返回相同的东西,但为什么呢?我也注意到范围,例如

for i in range(10):
    print(someClass())
Run Code Online (Sandbox Code Playgroud)

当快速调用类时,python是否有任何特殊原因?我甚至不知道python是这样做的,还是可能是一个bug?如果它不是一个bug,有人可以向我解释如何修复它或一个方法,以便每次调用方法/类时它生成一个不同的id?我很困惑这是怎么做的,因为如果我等了,它确实会改变,但如果我尝试两次或更多次调用同一个类则不会.

lvc*_*lvc 39

id对象只保证是唯一的那个对象的生命周期内,不超过一个程序的整个生命周期.someClass您创建的两个对象仅在调用期间存在print- 之后,它们可用于垃圾收集(并且在CPython中,立即释放).由于它们的生命周期不重叠,因此共享id是有效的.

在这种情况下,由于两个CPython实现细节的组合,它也是没有改造的:首先,它通过引用计数进行垃圾收集(使用一些额外的魔法来避免循环引用的问题),其次,id对象的相关性与变量的底层指针的值(即其内存位置).因此,第一个对象,即最近分配的对象,立即被释放 - 分配的下一个对象最终将在同一位置上并不太令人惊讶(尽管这可能还取决于解释器编译方式的细节) ).

如果你依赖于具有不同ids的几个对象,你可以将它们放在一起 - 例如,在列表中,以便它们的生命周期重叠.否则,您可能会实现具有不同保证的特定于类的ID - 例如:

class SomeClass:
    next_id = 0

    def __init__(self):
         self.id = SomeClass.nextid
         SomeClass.nextid += 1
Run Code Online (Sandbox Code Playgroud)

  • 很好的解释,但一个小狡辩.它的编写方式意味着内存实际上是'free`d然后是`malloc`d(或者一些等价物),当它真的没有超出Python的PyObject自由列表时,而且_shat_为什么它如此一致地发生(取决于你解释得很好的警告),甚至跨平台或调试mallocs等等. (5认同)
  • 基础`object``tp_dealloc`调用[堆类型的`tp_free`](http://hg.python.org/cpython/file/c3896275c0f6/Objects/typeobject.c#l2370),即[`PyObject_GC_Del`]( http://hg.python.org/cpython/file/c3896275c0f6/Modules/gcmodule.c#l1621)。这反过来使用宏`PyObject_FREE`。关于 CPython 如何编译的警告是 [没有 pymalloc](http://hg.python.org/cpython/file/c3896275c0f6/Include/objimpl.h#l133) 宏`PyObject_FREE` 被定义为`PyMem_FREE `,对于非调试版本,它只是“免费”。所以那时地址重用取决于平台`malloc`。 (2认同)

aba*_*ert 14

如果您阅读文档id,它说:

返回对象的"标识".这是一个整数,在该生命周期内保证该对象是唯一且恒定的.具有非重叠寿命的两个对象可以具有相同的id()值.

而这正是发生的事情:你有两个生命周期不重叠的对象,因为第一个对象在创建第二个对象之前已经超出了范围.


但不要相信这种情况总会发生.特别是如果你需要处理其他Python实现,或者更复杂的类.所有的语言说的是,这两个对象可以具有相同的id()价值,而不是他们.他们这样做的事实取决于两个实现细节:

  • 垃圾收集器必须在代码开始分配第二个对象之前清理第一个对象 - 这可以保证在CPython或任何其他引用计数实现时发生(当没有循环引用时),但不太可能有代际垃圾收集器,如Jython或IronPython.

  • 封面下的分配器必须非常强烈地倾向于重用最近释放的相同类型的对象.在CPython中也是如此,它在基本C之上有多层花哨的分配器malloc,但是大多数其他实现在底层虚拟机上留下了更多.


最后一件事:object.__repr__碰巧包含一个恰好与id十六进制数相同的子串的事实只是CPython的一个实现工件,无法保证在任何地方.根据文件:

如果可能的话,这应该看起来像一个有效的Python表达式,可用于重新创建具有相同值的对象(给定适当的环境).如果无法做到这一点,<...some useful description…>则应返回表单的字符串.

事实上CPython object恰好放hex(id(self))(实际上,我相信它正在做相当于 - sprintf它的指针通过%p,但由于CPython id只是返回相同的指针强制转换为long最终是相同的)在任何地方都无法保证.即使它已经object存在,因为... 甚至在2.x天的早期存在之前.在交互式提示下,你可以安全地依赖它来进行这种简单的"在这里发生什么"的调试,但不要试图在它之外使用它.