在Python中,两个整数具有相同的id,但列表或元组不具备相同的id。

59
在Python中,两个整数具有相同的id
a = 10
b = 10
a is b
>>> True

如果我拿两个列表:
a = [1, 2, 3]
b = [1, 2, 3]
a is b
>>> False

根据Senderle的回答,不可变对象引用具有相同的id,而可变对象(如列表)具有不同的id。
所以现在根据他的回答,元组应该具有相同的id - 这意味着:
a = (1, 2, 3)
b = (1, 2, 3)
a is b
>>> False

理想情况下,由于元组是不可变的,它应该返回True,但它却返回了False!这是什么解释?

你必须区分变量和对象(列表和元组)- 当你像这样比较两个对象时,你比较的是它们的内存地址,尽管它们具有相同的列表和元组元素。 - dmitryro
15
我认为您误解了您提供链接的答案。不同的不可变对象具有不同的id。但对于小于256的整数(在大多数解释器上),这些值已经预加载到内存中,因此在您的第一个示例中,a等于b。 - Jacques Gaudin
谢谢@JacquesGaudin!现在我有了一些理解。 - Ram Vallury
1
无法保证是否进行了这种优化。你应该停止考虑这个问题,有时候 is 会返回 True,而有时候不会。如果你想确保它返回 True,只需执行以下操作:b = ab = tuple(a)(如果将元组作为参数传递给 tuple 调用,则会返回对该参数的引用)。 - Bakuriu
我重新打开这个问题,因为重复的目标没有解决这个问题的主要观点,即可变性是否对身份有影响。 - Dimitris Fasarakis Hilliard
那不是链接答案所说的。链接答案明确表示,可以拥有两个具有相同值的不同不可变对象。它继续说,无论是一个对象还是两个对象都没有关系,因为问题(由于别名)只会在更改对象时发生,而不可变对象不能被更改(这就是“不可变”的含义)。 - Karl Knechtel
5个回答

88

不可变对象的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/

4
@Ram,你认为它应该返回True,但它们是两个不同的对象(整数),但具有相同的值(而非id)。请注意它们不小于256。 - Mazdak
1
是的,我同意。我认为只有小于256的整数才会返回ids()为真。在Python解释器中,大于256的任何整数都将具有自己的ID空间,但值将相同。 - Ram Vallury
10
所有的8位小数字都指向内存中相同的对象这一事实是CPython实现的一个细节,不应因此依赖它。 - cat
2
@Ram,我认为你从错误的角度看待了这种情况。你应该问自己为什么它对于a[0]b[0]返回True,而不是为什么它对于a[1]b[1]返回False。如果你想了解更多相关信息,请参见我的这个答案 - Bakuriu
1
@stillanoob 嗯,有什么问题吗?:)你的代码是在运行时创建了不同命名空间和对象的混乱。请解释一下这里的确切问题或提出一个相关问题。 - Mazdak
显示剩余5条评论

25

Immutable != 同一对象。*

不可变对象是指其状态无法更改的对象;仅此而已。当创建新对象时,将为其分配一个新地址因此,使用is检查地址是否相等将返回False

1 is 1"a" is "a"返回True的事实是由Python执行的整数缓存字符串驻留所致,请不要让它困惑您;它与所讨论的对象是可变/不可变无关。


*空的不可变对象确实引用同一个对象(参见),它们的is结果会返回true,尽管这是一种特殊的实现情况。


20

看一下这段代码:

>>> a = (1, 2, 3)
>>> b = (1, 2, 3)
>>> c = a
>>> id(a)
178153080L
>>> id(b)
178098040L
>>> id(c)
178153080L
为了弄清楚为什么a is c被评估为True,而a is b却得到False的结果,我强烈建议您在在线Python导师中逐步运行上面的代码片段。内存中对象的图形表示将为您提供更深入的洞察力(我附上了一个截图)。 enter image description here

1
根据文档,不可变对象可能具有相同的id,但不能保证它们一定相同。可变对象始终具有不同的id。

https://docs.python.org/3/reference/datamodel.html#objects-values-and-types

类型影响对象行为的几乎所有方面。某种意义上,甚至会影响对象标识的重要性:对于不可变类型,计算新值的操作实际上可能返回任何具有相同类型和值的现有对象的引用,而对于可变对象则不允许这样做。
在 Python 的早期版本中(Python 3.7 之前),元组分配了不同的 ID。
从 Python 3.7 开始,使用相同元组分配的两个变量可能具有相同的 ID。
>>>a = (1, 2, 3)
>>>b = (1, 2, 3)
>>>a is b
True

整数大于256也有不同的ID:
>>>a = 123
>>>b = 123
>>>a is b
True
>>>
>>>a = 257
>>>b = 257
>>>a is b
False

0

请检查以下代码... 当我们将旧值重新赋给元组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)
>>> 

请查看链接为什么当赋予相同的值时,元组没有获得相同的ID?并在阅读后了解更多信息。另外,这里还讨论了另一种情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接