Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/342.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么Python类的id在快速调用时不是唯一的?_Python_Class_Python 3.x - Fatal编程技术网

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

为什么Python类的id在快速调用时不是唯一的?,python,class,python-3.x,Python,Class,Python 3.x,我正在用Python(3.3.3)做一些事情,我遇到了一些让我困惑的事情,因为我理解类在每次调用它们时都会得到一个新的id 假设您在某个.py文件中有: class someClass: pass print(someClass()) print(someClass()) 上面返回的是同一个id,这让我很困惑,因为我正在调用它,所以它不应该是相同的,对吗?当同一个类在一行中被调用两次时,Python就是这样工作的吗?当我等待几秒钟的时候,它会给出一个不同的id,但是如果我像上面的例子一样在同

我正在用Python(3.3.3)做一些事情,我遇到了一些让我困惑的事情,因为我理解类在每次调用它们时都会得到一个新的id

假设您在某个.py文件中有:

class someClass: pass

print(someClass())
print(someClass())
上面返回的是同一个id,这让我很困惑,因为我正在调用它,所以它不应该是相同的,对吗?当同一个类在一行中被调用两次时,Python就是这样工作的吗?当我等待几秒钟的时候,它会给出一个不同的id,但是如果我像上面的例子一样在同一时间这样做,它似乎不会那样工作,这让我很困惑

>>> print(someClass());print(someClass())
<__main__.someClass object at 0x0000000002D96F98>
<__main__.someClass object at 0x0000000002D96F98>

当类被快速调用时,Python这样做有什么特别的原因吗?我甚至不知道Python做了这件事,或者它可能是一个bug?如果它不是一个bug,有人能向我解释一下如何修复它或一个方法,这样每次调用该方法/类时它都会生成一个不同的id吗?我对它是如何实现的感到非常困惑,因为如果我等待,它会改变,但如果我尝试调用同一个类两次或更多次,它不会改变。

它会释放第一个实例,因为它没有被保留,然后因为在此期间内存没有发生任何变化,它会再次实例化到同一个位置。

试试这个,请尝试调用以下命令:

a = someClass()
for i in range(0,44):
    print(someClass())
print(a)

你会看到一些不同的东西。为什么?原因“foo”循环中第一个对象释放的内存被重用。另一方面,
a
不会被重用,因为它会被保留。

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

在这种情况下,由于结合了两个CPython实现细节,它也不引人注目:首先,它通过引用计数进行垃圾收集(使用一些额外的魔法来避免循环引用的问题),其次,对象的
id
与变量的底层指针的值相关(即,它的内存位置)。因此,第一个对象,即最近分配的对象,会立即被释放——下一个分配的对象会在同一个位置结束也就不足为奇了(尽管这可能还取决于解释器是如何编译的)

如果您依赖于多个具有不同
id
s的对象,则可以将它们保留在周围(例如,在列表中),以便它们的生命周期重叠。否则,您可能会实现具有不同保证的类特定id-例如:

class SomeClass:
    next_id = 0

    def __init__(self):
         self.id = SomeClass.nextid
         SomeClass.nextid += 1

如果您阅读了的文档,它会说:

返回对象的“标识”。这是一个整数,保证该对象在其生存期内唯一且恒定。具有非重叠生存期的两个对象可能具有相同的
id()
值。

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


但也不要相信这种情况总是会发生。尤其是当您需要处理其他Python实现或更复杂的类时。该语言所说的只是这两个对象可能具有相同的
id()
值,而不是它们将具有相同的值。它们的实际情况取决于两个实现细节:

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

  • 隐藏的分配器必须非常倾向于重用最近释放的相同类型的对象。这在CPython中是正确的,它在基本C
    malloc
    之上有多层奇特的分配器,但大多数其他实现留给底层虚拟机的更多


最后一件事:
对象。\uuu repr\uuu
恰好包含一个子字符串,该子字符串恰好与
id
的十六进制数相同,这只是CPython的一个实现工件,在任何地方都不能保证。根据:

如果可能的话,这应该是一个有效的Python表达式,可以用来重新创建具有相同值的对象(给定适当的环境)。如果不可能,则应返回一个
格式的字符串


CPython的
对象
恰好放置了
hex(id(self))
(事实上,我相信它的作用相当于
sprintf
——通过
%p
,但是由于CPython的
id
只是将相同的指针转换为
long
,结果是相同的)在任何地方都不能保证。即使它在2.x早期的
object
之前是真的。你可以放心地依靠它来完成这种简单的“这里发生了什么”在交互式提示下调试,但不要尝试使用它。

我感觉这里有一个更深层次的问题。在程序的整个生命周期中,您不应该依靠
id
来跟踪唯一的实例。您只需将其视为每个对象实例持续时间内的非保证内存位置指示器。如果立即创建并释放实例,然后您可以在同一内存位置创建连续的实例

也许您需要做的是跟踪一个类静态计数器,该计数器为每个新实例分配一个唯一的id,并增加n的类静态计数器
class SomeClass:
    next_id = 0

    def __init__(self):
         self.id = SomeClass.nextid
         SomeClass.nextid += 1
print([someClass() for i in range(10)])