【问题标题】:List comprehension assignment/comparison fails after 256 [duplicate]列表理解分配/比较在 256 之后失败 [重复]
【发布时间】:2015-07-08 10:35:27
【问题描述】:

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

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。

【问题讨论】:

  • 因为CPython中缓存了小整数,所以使用!=而不是is操作符。

标签: python list variable-assignment


【解决方案1】:

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

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

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

不用说,您不应该依赖这种优化。


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

** 如果您想知道这在 CPython 中是如何工作的,在 C API 级别,PyLong_FromLong 通过在单值数组中查找从 -5 到 256 的数字来做到这一点。可以看到3.4版本的代码,例如here;宏 CHECK_SMALL_INT 和它调用的实际函数 get_small_int 以及函数使用的静态数组都在同一个文件中,靠近顶部。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-02
    • 2021-02-10
    • 1970-01-01
    • 2022-06-30
    • 2021-04-20
    • 1970-01-01
    • 1970-01-01
    • 2021-10-20
    相关资源
    最近更新 更多