列表理解 assignment/comparison 在 256 之后失败

List comprehension assignment/comparison fails after 256

我试图找到列表的切片分配和常规分配之间的性能差异。这是代码:

import time

N =  1000  
a = list(range(N))
b = list(range(N))

time1 = time.time()
for i in range(N):
    a = [x for x in a if x is not i]
time2 = time.time()
for i in range(N):
    b[:] = [x for x in b if x is not i]
time3 = time.time()

print a
print b    
print time2 - time1
print time3 - time2

我的期望是,对于每个列表 ab,这将一次删除一个元素,因此 print aprint b 都打印为空列出。相反,他们似乎总是打印起始列表,但缺少前 256 个元素。

他们都打印:

[257, 258, 259 ... N-1]

发生了什么事?

我正在使用 Python 2.7.6.

问题是您使用的是 is 而不是 ==

前者检查对象身份,而不是相等性。没有理由相信评估 300+1 两次会给你相同的 int 对象,只是它们都会给你 int 个值为 301 的对象.

对于最大 256 的数字,"work" 会发生这种情况,因为您的特定 Python 实现 * 恰好对最大 256 的整数进行实习。在启动时,它为数字 1 创建一个单例对象,为 2 创建一个单例对象,依此类推。任何时候表达式的计算结果为数字 1,它都会为您提供该对象,而不是新对象。**

不用说,你不应该依赖那个优化。


* IIRC,从 1.x 天到 3.5 的每个版本的 CPython 默认为从 -5 到 256 的所有整数的这种行为,但你可以改变这些在构建时限制或关闭该功能,不同的实现可能会做一些不同的事情。

** 如果您想知道这在 CPython 中是如何工作的,在 C API 级别,PyLong_FromLong does this by looking up numbers from -5 to 256 in an array of singleton values. You can see the 3.4 version of the code, for example, here;宏 CHECK_SMALL_INT 和它调用的实际函数 get_small_int 以及函数使用的静态数组都在同一个文件中,靠近顶部。