在python中,两个代码具有不同的结果:
a = 300
b = 300
print (a==b)
print (a is b) ## print True
print ("id(a) = %d, id(b) = %d"%(id(a), id(b))) ## They have same address
Run Code Online (Sandbox Code Playgroud)
但是在外壳模式(交互模式)下:
>>> a = 300
>>> b = 300
>>> a is b
False
>>> id(a)
4501364368
>>> id(b)
4501362224
Run Code Online (Sandbox Code Playgroud)
“是”运算符具有不同的结果。
当您在运行代码.py的脚本,将整个文件被编译成一个代码对象之前执行它。在这种情况下,CPython可以进行某些优化-例如将相同的实例重新使用整数300。
您还可以在REPL中通过在更类似于脚本执行的上下文中执行代码来重现该代码:
>>> source = """\
... a = 300
... b = 300
... print (a==b)
... print (a is b)## print True
... print ("id(a) = %d, id(b) = %d"%(id(a), id(b))) ## They have same address
... """
>>> code_obj = compile(source, filename="myscript.py", mode="exec")
>>> exec(code_obj)
True
True
id(a) = 140736953597776, id(b) = 140736953597776
Run Code Online (Sandbox Code Playgroud)
其中一些优化是非常积极的。您可以修改脚本行,b = 300将其更改为b = 150 + 150,而CPython仍将“折叠” b为相同的常量。如果您对此类实现细节感兴趣,请查看peephole.c并在Ctrl + F中查找“ consts table”。
相反,当您直接在REPL中逐行运行代码时,它将在不同的上下文中执行。每行以“单”模式编译,因此无法进行此优化。
>>> scope = {}
>>> lines = source.splitlines()
>>> for line in lines:
... code_obj = compile(line, filename="<I'm in the REPL, yo!>", mode="single")
... exec(code_obj, scope)
...
True
False
id(a) = 140737087176016, id(b) = 140737087176080
>>> scope['a'], scope['b']
(300, 300)
>>> id(scope['a']), id(scope['b'])
(140737087176016, 140737087176080)
Run Code Online (Sandbox Code Playgroud)
关于 CPython 及其行为,实际上有两件事需要了解。首先,在[-5, 256]范围内的小整数在内部被嵌入。因此,即使在 REPL 中,落在该范围内的任何值都将共享相同的 id:
>>> a = 100
>>> b = 100
>>> a is b
True
Run Code Online (Sandbox Code Playgroud)
由于 300 > 256,它没有被实习:
>>> a = 300
>>> b = 300
>>> a is b
False
Run Code Online (Sandbox Code Playgroud)
其次,在脚本中,文字被放入编译代码的常量部分。Python 足够聪明,可以意识到,由于a和 都b
引用文字,300而且它300是一个不可变对象,因此它可以继续引用相同的常量位置。如果您稍微调整一下脚本并将其写为:
def foo():
a = 300
b = 300
print(a==b)
print(a is b)
print("id(a) = %d, id(b) = %d" % (id(a), id(b)))
import dis
dis.disassemble(foo.__code__)
Run Code Online (Sandbox Code Playgroud)
输出的开头部分如下所示:
2 0 LOAD_CONST 1 (300)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 1 (300)
6 STORE_FAST 1 (b)
...
Run Code Online (Sandbox Code Playgroud)
如您所见,CPython 正在加载a和b使用相同的常量槽。这意味着,a和b现在指的是同一个对象(因为它们引用相同的插槽),这就是为什么a is b是True在脚本,但不是在REPL。
如果将语句包装在函数中,您也可以在 REPL 中看到这种行为:
>>> import dis
>>> def foo():
... a = 300
... b = 300
... print(a==b)
... print(a is b)
... print("id(a) = %d, id(b) = %d" % (id(a), id(b)))
...
>>> foo()
True
True
id(a) = 4369383056, id(b) = 4369383056
>>> dis.disassemble(foo.__code__)
2 0 LOAD_CONST 1 (300)
2 STORE_FAST 0 (a)
3 4 LOAD_CONST 1 (300)
6 STORE_FAST 1 (b)
# snipped...
Run Code Online (Sandbox Code Playgroud)
底线:虽然 CPython 有时会进行这些优化,但您不应该真正指望它——它确实是一个实现细节,并且随着时间的推移而发生了变化(CPython 过去只对最多 100 的整数执行此操作,因为例子)。如果您要比较数字,请使用==. :-)