我写了三行相同的代码并得到不同的结果,首先我在一个交互式shell中运行它:
>>> a = 10000
>>> b = 10000
>>> a is b
False
>>> a = 10000; b = 10000; a is b
True
然后我有一个包含以下内容的Python文件:
a = 10000
b = 10000
print a is b
我运行它并获得True
我的Python环境:
Python 2.7.5 (default, Mar 9 2014, 22:15:05)
[GCC 4.2.1 Compatible Apple LLVM 5.0 (clang-500.0.68)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
这里发生了什么?每个人都在谈论编译,有没有人知道交互式shell如何编译和运行这些代码行?
答案 0 :(得分:3)
如果你把任何一个放入一个函数中,它也会评估True
。发生的事情是Python生成一个在将函数编译成字节码时使用的常量列表,并且相等的常量将“折叠”为一个加载两次的值。在编译一行代码*
所以这里是使用dis
获得的其中一个函数的字节码 - 对于行号除之外的任何方法实际上都是相同的,所以我不在这里复制。< / p>
2 0 LOAD_CONST 1 (10000)
3 STORE_FAST 0 (a)
6 LOAD_CONST 1 (10000)
9 STORE_FAST 1 (b)
12 LOAD_FAST 0 (a)
15 LOAD_FAST 1 (b)
18 COMPARE_OP 8 (is)
21 RETURN_VALUE
这是为了:
def func():
a = 10000; b = 10000; return a is b
from dis import dis
dis(func)
请注意,两个LOAD_CONST
行都有相同的参数。这是对func.__code__.co_consts
中的索引的引用,它是一个元组。该元组的元素1是int对象10000
。
为了完整起见,如果你a = 10000; b = 10000; a is b
,这是原始单行compile()
的反汇编:
1 0 LOAD_CONST 0 (10000)
3 STORE_NAME 0 (a)
6 LOAD_CONST 0 (10000)
9 STORE_NAME 1 (b)
12 LOAD_NAME 0 (a)
15 LOAD_NAME 1 (b)
18 COMPARE_OP 8 (is)
21 POP_TOP
22 LOAD_CONST 1 (None)
25 RETURN_VALUE
除了行号/常数,NAME
与FAST
以及POP_TOP
的结尾之外,它基本相似。然而,如果您在单独的行上分配值,那么它不会使用常量执行此操作,因此每次都会创建一个新的int对象。
*为了增加一点吸引力,如果我将单行版本放入我的IPython笔记本中,a is b
为False
。
答案 1 :(得分:1)
我认为这是编译时优化。在第一种情况下,您正在编译a = 10000
然后编译b = 10000
,因此字节码编译器没有(简单)方法来确定它们的身份。
在其他情况下,编译器会看到a
和b
使用相同的文字初始化,之后不会更改。
这与小整数优化无关。那个也适用于表达式,即
>> a = 256; b = 256; a is b
True
但
>> a = 256; b = 256; a + 1 is b + 1
False
相应的代码是Python的窥孔优化的一部分(参见https://github.com/python/cpython/blob/master/Python/peephole.c)。
答案 2 :(得分:0)
答案 3 :(得分:0)
我只是想通了,实际上这些具有相同id的10000位于编译的代码对象::
中>>> code = compile("a = 10000; b = 10000; a is b", "<string>", "exec")
>>> code.co_consts
(10000, None)
编译器进行一些优化,10000只创建一次,因为10000是不可变的:)