还有一个问题,在现有的任何答案中都没有指出。Python允许合并任何两个不可变的值,并且预先创建的小int值并不是发生这种情况的唯一方式。Python实现从来不保证这样做,但它们都不仅仅是对小int型进行这样做。
首先,还有一些其他预先创建的值,比如空元组、str和bytes,以及一些短字符串(在CPython 3.6中,它是256个单字符Latin-1字符串)。例如:
>>> a = ()
>>> b = ()
>>> a is b
True
但是,即使非预先创建的值也可以是相同的。考虑以下例子:
>>> c = 257
>>> d = 257
>>> c is d
False
>>> e, f = 258, 258
>>> e is f
True
这并不局限于int值:
>>> g, h = 42.23e100, 42.23e100
>>> g is h
True
显然,CPython没有为42.23e100提供预先创建的浮点值。那么,这里发生了什么?
CPython编译器会在同一个编译单元中合并一些已知不可变类型的常量值,比如int、float、str、bytes。对于一个模块,整个模块是一个编译单元,但在交互式解释器中,每条语句都是一个单独的编译单元。因为c和d是在单独的语句中定义的,所以它们的值不会合并。由于e和f在同一个语句中定义,它们的值被合并。
您可以通过分解字节码来了解发生了什么。试着定义一个函数执行e, f = 128, 128然后调用dis。dis,你会看到只有一个常量值(128,128)
>>> def f(): i, j = 258, 258
>>> dis.dis(f)
1 0 LOAD_CONST 2 ((128, 128))
2 UNPACK_SEQUENCE 2
4 STORE_FAST 0 (i)
6 STORE_FAST 1 (j)
8 LOAD_CONST 0 (None)
10 RETURN_VALUE
>>> f.__code__.co_consts
(None, 128, (128, 128))
>>> id(f.__code__.co_consts[1], f.__code__.co_consts[2][0], f.__code__.co_consts[2][1])
4305296480, 4305296480, 4305296480
你可能会注意到编译器将128作为一个常量存储,尽管字节码实际上并没有使用它,这让你知道CPython编译器所做的优化是多么少。这意味着(非空的)元组实际上不会被合并:
>>> k, l = (1, 2), (1, 2)
>>> k is l
False
把它放到一个函数中,dis它,然后看看co_consts -有一个1和一个2,两个(1,2)元组共享相同的1和2,但不相同,还有一个((1,2),(1,2))元组,它有两个不同的相等元组。
CPython还做了一项优化:字符串实习。与编译器常量折叠不同,这并不局限于源代码字面量:
>>> m = 'abc'
>>> n = 'abc'
>>> m is n
True
另一方面,它仅限于str类型,以及内部存储类型为"ascii compact"、"compact"或"legacy ready"的字符串,并且在许多情况下只有"ascii compact"会被捕获。
无论如何,关于什么值必须是、可能是或不能是不同的规则在不同的实现之间、在相同实现的版本之间、甚至在相同实现的同一副本上运行相同代码之间都是不同的。
为了好玩,学习特定Python的规则是值得的。但是在代码中依赖它们是不值得的。唯一安全的规则是:
不要编写假定两个相同但单独创建的不可变值相同的代码(不要使用x is y,使用x == y)
不要编写假定两个相同但分别创建的不可变值是不同的代码(不要使用x不是y,使用x != y)
或者,换句话说,唯一的用途是测试文档化的单例对象(如None)或只在代码中的一个地方创建的单例对象(如_sentinel = object()习惯用法)。