元组解包与普通赋值有何不同?

8

这个链接我了解到:

当前的实现方式为所有介于-5和256之间的整数保留一个整数对象数组,当您在该范围内创建一个整数时,您实际上只会得到对现有对象的引用。

但是,当我尝试为我的会话提供一些示例时,我发现它在赋值和元组拆包方面的行为不同。

以下是代码片段:

>>> a,b = 300,300
>>> a is b
True
>>> c = 300
>>> d = 300
>>> c is d
False

4
对于整数类型,is 的行为未被定义。任何您今天获得的有关某个实现行为的知识都将是脆弱的。 - Paul Hankin
1
@匿名:「实现定义」比「未定义」更准确,但关于脆弱性的说法仍然成立。 - Eric
可能是Python 2.6中的奇怪整数缓存的重复问题。虽然不完全相同,但被接受的答案(免责声明:我的)展示了元组情况下发生的事情,我认为这足以回答你的问题:这就是所谓的窥视优化器,它避免了常量的重复。 - Bakuriu
谢谢@Bakuriu,找到了一些有趣的解释。 - James Sapam
2个回答

7
由于int是不可变的,Python可能会或可能不会使用现有对象。如果您将以下代码保存到脚本文件中并运行它,则会输出两个True。
a, b = 300, 300
print a is b

c = 300
d = 300
print c is d

当Python编译代码时,它可能会重用所有常量。因为你在Python会话中输入了代码,代码是逐行编译的,所以Python无法将所有常量作为一个对象重复使用。
文档只说-5到256之间只有一个实例,但并没有定义其他情况的行为。对于不可变类型,使用isis not并不重要,因为你无法修改它们。

你可以在解释器提示符上模拟这个过程,将所有的代码放在一个函数中,然后运行它(而不是逐个运行它们)。 - Noufal Ibrahim

2
 import dis

 def testMethod1():
     a, b = 300, 300

 print dis.dis(testMethod1)

输出:

4 0 LOAD_CONST 2 ((300, 300))
3 UNPACK_SEQUENCE 2
6 STORE_FAST 0 (a)
9 STORE_FAST 1 (b)
12 LOAD_CONST 0 (None)
15 RETURN_VALUE None

 def testMethod2():
     a = 300
     b = 300

打印:

7 0 LOAD_CONST 1 (300)
3 STORE_FAST 0 (a)
8 6 LOAD_CONST 1 (300)
9 STORE_FAST 1 (b)
12 LOAD_CONST 0 (None)
15 RETURN_VALUE None

因此,看起来两种方法基本相同,但第一种方法中有一个步骤使用了LOAD_CONST,而第二种方法中使用了两个步骤....

编辑
经过一些测试,我发现两种方法最终都会返回False; 然而,在仅运行一次的情况下(即不将方法放在循环中),它们似乎总是返回True。有时它使用单个引用,有时它不使用。

文档仅说明-5到256将返回相同的引用;因此,在这种情况下,你根本不应该使用is进行比较,因为数字的当前id没有保证。

NB:你永远不应该使用is来比较值,因为这不是它的作用,它是用来比较标识的。我的观点是,当你超出定义的范围时,is的返回值并不总是True


嗨 Steve P,我在osx上测试过了,Python 2.7.2版本。 - James Sapam
这取决于Python的实现。分两步加载可以给低级缓存留更多的空间。缓存[-5,256]在Python代码中发生,但是不可变对象(如int)可以被重用。你不能完全依赖这些缓存。 - iurisilvio
1
关于你最后一段的内容,is不应该用于“比较”。is应该用于“身份识别”。比较a和b就像是在说“a等于b吗?”测试身份就像是在问“a和b是同一件事吗?” - Noufal Ibrahim
@NoufalIbrahim 我明白 is 的作用。但我所说的是,在这种情况下,你不想使用 is 来进行比较。 - Steve P.

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