Python 中的两个变量具有相同的 id,但不是列表或元组

Posted

技术标签:

【中文标题】Python 中的两个变量具有相同的 id,但不是列表或元组【英文标题】:Two variables in Python have same id, but not lists or tuples 【发布时间】:2016-11-06 11:27:54 【问题描述】:

Python 中的两个变量有相同的id:

a = 10
b = 10
a is b
>>> True

如果我拿两个lists:

a = [1, 2, 3]
b = [1, 2, 3]
a is b
>>> False

根据this linkSenderle 的回答,不可变对象引用具有相同的 id,而可变对象(如列表)具有不同的 id。

所以现在根据他的回答,元组应该具有相同的 id - 含义:

a = (1, 2, 3)
b = (1, 2, 3)
a is b
>>> False

理想情况下,由于元组不可变,它应该返回True,但它返回的是False

解释是什么?

【问题讨论】:

你必须区分变量和对象(列表和元组)——当你比较像这样的两个对象时,你会比较它们不同的内存地址,尽管有相同的列表和元组元素。 我认为您误解了您链接到的答案。不同的不可变对象具有不同的 id。但是对于小于 256 的整数(在大多数解释器上),该值已预先加载到内存中,因此在您的第一个示例中 a 是 b 谢谢@JacquesGaudin!这让我现在有了一些理解。 是否进行这种优化不保证。你应该停止思考这个问题,有时is 会返回True 而其他时候不会。如果您想确保它返回 True,只需执行以下操作:b = ab = tuple(a)(如果将元组作为参数传递,tuple 调用将返回对参数的引用)。 我重新打开这个是因为 dup 目标没有解决这个问题的要点,即可变性是否对身份有影响。 【参考方案1】:

不可变对象没有相同的id,事实上,这对于您单独定义的任何类型的对象都不是这样。一般来说,每次在 Python 中定义对象时,都会创建一个具有新标识的新对象。但是,为了优化(大部分),对于小整数(介于 -5 和 256 之间)和内部字符串有一些例外,它们具有特殊的长度——通常少于 20 个字符——*是单例并且具有相同的id(实际上是一个具有多个指针的对象)。你可以像下面这样检查:

>>> 30 is (20 + 10)
True
>>> 300 is (200 + 100)
False
>>> 'aa' * 2 is 'a' * 4
True
>>> 'aa' * 20 is 'a' * 40
False

对于自定义对象:

>>> class A:
...    pass
... 
>>> A() is A() # Every time you create an instance you'll have a new instance with new identity
False

还要注意is 运算符将检查对象的身份,而不是值。如果你想检查你应该使用==的值:

>>> 300 == 3*100
True

由于没有针对元组或任何可变类型的优化或实习规则,因此如果您定义两个相同大小的元组,它们将获得自己的身份,从而获得不同的对象:

>>> a = (1,)
>>> b = (1,)
>>>
>>> a is b
False

还值得一提的是,“单例整数”和“内部字符串”规则即使在迭代器中定义也是如此。

>>> a = (100, 700, 400)
>>>
>>> b = (100, 700, 400)
>>>
>>> a[0] is b[0]
True
>>> a[1] is b[1]
False

* 一篇很好的详细文章:http://guilload.com/python-string-interning/

【讨论】:

在最后提到的例子中(元组):a[1] is b[1] 应该是 True,为什么它返回 False? @Ram 为什么你认为它应该返回 True,它们是 2 个独立的对象(整数)但具有相同的值(不是 id)。请注意,它们不小于 256。 是的,同意。我认为只有小于 256 的整数才会返回 ids() true。 python解释器中任何大于256的整数都有自己的id空间,值将相同。 所有小的 8 位数字都指代内存中的相同对象这一事实是 CPython 的实现细节,不应以任何理由依赖 @Ram 我相信您从错误的角度看待这种情况。你应该问自己为什么它为a[0]b[0] 返回True,而不是为什么它为a[1]b[1] 返回False。如果您想了解更多详情,请参阅this related answer of mine。【参考方案2】:

不可变的!= 同一个对象。*

An immutable object is simply an object whose state cannot be altered; 仅此而已。 创建新对象时,将为其分配一个新地址因此,检查地址是否与is 相等将返回False

1 is 1"a" is "a" 返回True 是由于integer caching 和字符串interning 由Python 执行的,所以不要让它混淆你;它与所讨论的对象可变/不可变无关。


*空不可变对象 do refer to the same object 和它们的 isness 确实返回 true,不过这是一个特殊的实现特定情况。

【讨论】:

【参考方案3】:

看看这段代码:

>>> a = (1, 2, 3)
>>> b = (1, 2, 3)
>>> c = a
>>> id(a)
178153080L
>>> id(b)
178098040L
>>> id(c)
178153080L

为了弄清楚为什么a is c 被评估为Truea is b 产生False 我强烈建议您在Online Python Tutor 中逐步运行上面的sn-p。内存中对象的图形表示将使您对这个问题有更深入的了解(我附上了屏幕截图)。

【讨论】:

【参考方案4】:

检查下面的代码.. tupils ab 在我们重新分配了它们的旧值后,将保留它们旧的引用 (ID)。 (但是,列表不会出现这种情况,因为它们是可变的)

最初 ab 具有相同的值 ( (1,2) ),但它们的 ID 不同。更改它们的值后,当我们将值 (1,2) 重新分配给 ab 时,它们现在引用了他们自己的相同 ID(分别为 88264264 和 88283400)。

>>> a = (1,2)
>>> b = (1,2)
>>> a , b
((1, 2), (1, 2))
>>> id(a)
88264264
>>> id(b)
88283400
>>> a = (3,4)
>>> b = (3,4)
>>> id(a)
88280008
>>> id(b)
88264328
>>> a = (1,2)
>>> b = (1,2)
>>> id(a)
88264264
>>> id(b)
88283400
>>> a , b
((1, 2), (1, 2))
>>> id(a) , id(b)
(88264264, 88283400)
>>> 

**查看链接Why don't tuples get the same ID when assigned the same values? 也读过这个。这里还讨论了另一种情况。

【讨论】:

以上是关于Python 中的两个变量具有相同的 id,但不是列表或元组的主要内容,如果未能解决你的问题,请参考以下文章

在ggplot2中绘制两个具有相同y变量但不同x变量的箱线图

OBIEE 创建自定义联接以提取两个相同但具有不同数据的列

如果它们都具有相同的 id,如何将来自 sql 查询的图像存储在不同的变量下?

BQ数组查找:类似于NTH,但基于索引,而不是位置

比较id的数据

连接两个具有相同键但不同字段的表