为什么 is 运算符说两个相等的 id 不一样



按照SQLAlchemy教程,我在控制台上进行了黑客攻击,并得到了以下结果:

>>> ed_user
<User(name='ed', fullname='Ed Jones', password='edspassword')>
>>> id(ed_user)
139859764807552
>>> our_user
<User(name='ed', fullname='Ed Jones', password='edspassword')>
>>> id(our_user)
139859764807552
>>> ed_user is our_user
True # so ed_user and our_user is the same object
>>> id(ed_user) - id(our_user)
0 # not suprisingly, their ids (i.e. their position in memory) don't differ
>>> id(ed_user) is id(our_user)
False # WAT?!
>>> id(ed_user) == id(our_user)
True # okay, as integer values they are the same
>>> id(id(ed_user)) == id(id(our_user))
True # but their id's ids are also the same

为什么id(ed_user) is id(our_user) False

您正在创建整数,检查其id(),然后再次丢弃整数。然后,Python 会为您创建的下一个整数重用内存地址,以便它们的id()值匹配。

id()函数文档中:

具有

非重叠生存期的两个对象可能具有相同的id()值。

Python 对象的生存期由其引用计数决定;如果对某个对象的引用超过 0 个,则该对象将保留在内存中,否则将被删除。表达式中的内部id()调用id(id(our_user))创建一个仅由堆栈引用的整数;它被传递给外部id()调用,然后从堆栈中删除,之后没有对它的引用,它再次被丢弃。

如果您首先创建了对生成的整数值的引用,您会发现它们并不相同:

>>> a = b = []
>>> id(a) == id(b)
True
>>> a is b
True
>>> id_of_a, id_of_b = id(a), id(b)
>>> id_of_a == id_of_b
True
>>> id(id_of_a) == id(id_of_b)
False

但是等等!对于整数(从 -5 到 256(,Python 只会创建一个副本(实习(,因此两个整数的恒等式将再次相同:

>>> small = 200
>>> small is 200
True

总而言之,除非您绝对知道必须测试单例对象(对象标识(,否则请坚持使用相等性测试。这也适用于SQLAlchemy。

让我们一一浏览一下您的结果,看看发生了什么:

>>> ed_user is our_user
True

所以情况就是这样。我们有两个变量,它们引用同一个对象。这没什么特别的。您甚至可以通过执行ed_user = our_user = 'something'来复制它。

>>> id(ed_user) == id(our_user)
True

由于两个变量相同,因此 id() 的返回值与两个变量匹配。显然,差异也是零。

>>> id(ed_user) is id(our_user)
False

现在这就是它变得有趣的地方。我们知道返回值匹配,但根据此检查,返回值不是相同的对象。 id()返回一个整数,因此显然这两个整数并不相同。这怎么可能?嗯,这很简单:Python只为一小组小数字保留固定ID。对于所有较大的数字,将根据需要创建新的整数对象(实际对象!(。所以在您的情况下,数字是 139859764807552 .这肯定是一个很大的数字,所以 Python 将为 id(ed_user) 调用创建一个整数对象,...以及id(our_user)电话。所以我们有两个不同的整数对象。当然,两个不同的对象永远不会完全相同。目前为止,一切都好。

>>> id(id(ed_user)) == id(id(our_user))
True

现在这里变得疯狂了。上面,我们有两个 id() 调用,它们返回同时存在的整数对象(因为这是比较所必需的(。现在,id()保证我们永远不会为同时存在的两个不同对象返回相同的整数。上一次调用就是这种情况,这就是为什么我们最终有两个不同的整数对象(具有相同的值(。

但是在这里,一切都有点不同:我们首先调用id(ed_user),这给了我们一个 int 对象(我们称之为 a (的值139859764807552 。然后我们调用它id()并获取一个 int 对象作为 a 的 id ;让我们称之为id_a.此时,a不再被引用,也不需要用于比较(毕竟我们正在比较id_a(。所以Python丢弃了它。接下来,我们打电话给id(out_user)。这给了我们一个 int 对象 ( b (,也适用于值 139859764807552 。由于a已经不在了,b可以去a的地方,最终得到相同的id。所以我们在b上调用id()并返回一个整数对象id_b。由于ba处于相同的内存位置,并且由于 id 基于内存位置,因此id_a等于id_b(同样id_a is id_b

情况并非如此(。
我认为

当你提取引用时,python会选择一个地址名称并在使用它时将其抛出,并在您尝试再次提取时选择另一个地址。检查这里

In [71]: a = b = []
In [72]: a is b
Out[72]: True
In [73]: id(a) == id(b)
Out[73]: True
In [74]: id(a) - id(b)
Out[74]: 0
In [75]: id(a) is id(b)
Out[75]: False
In [76]: id(id(a)), id(id(b))
Out[76]: (45539872, 45539872)
In [77]: id(id(a))#--
Out[77]: 45540016   !
                    ! see here its picking another reference then it pick before
In [78]: id(id(b))#--!
Out[78]: 45539896

In [81]: id(id(a)), id(id(b)) # but here it's again same
Out[81]: (45539824, 45539824)
In [82]: id(id(a)) == id(id(b))
Out[82]: True
In [83]: id(a) is id(b)
Out[83]: False

最新更新