一个相当常见的操作是基于另一个list过滤一个list.人们很快发现:

[x for x in list_1 if x in list_2]

对于大的输入来说是很慢的——它是O(n*m).讨厌.我们如何加快速度?使用set进行筛选查找O(1):

s = set(list_2)
[x for x in list_1 if x in s]

这提供了良好的整体O(n)行为.然而,我经常看到,即使是资深的程序员也会跌入The Trap人的行列™:

[x for x in list_1 if x in set(list_2)]

阿克!这也是O(n*m),因为python构建了set(list_2)every次,而不是一次.


我以为故事就到此结束了——python无法将其优化为只构建一次set.只是要注意trap .必须接受它.隐马尔可夫模型.

#python 3.3.2+
list_2 = list(range(20)) #small for demonstration purposes
s = set(list_2)
list_1 = list(range(100000))
def f():
    return [x for x in list_1 if x in s]
def g():
    return [x for x in list_1 if x in set(list_2)]
def h():
    return [x for x in list_1 if x in {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19}]

%timeit f()
100 loops, best of 3: 7.31 ms per loop

%timeit g()
10 loops, best of 3: 77.4 ms per loop

%timeit h()
100 loops, best of 3: 6.66 ms per loop

python(3.3)can优化了一组文字.在这种情况下,它甚至比f()还要快,大概是因为它可以用LOAD_FAST代替LOAD_GLOBAL.

#python 2.7.5+
%timeit h()
10 loops, best of 3: 72.5 ms per loop

Python2显然没有进行这种优化.我试着进一步研究python3在做什么,但不幸的是,dis.dis无法探测理解表达式的内部.基本上所有有趣的东西都会变成MAKE_FUNCTION.

所以现在我想知道,为什么python 3可以.x优化设置文字,只构建一次,而不是set(list_2)次?

推荐答案

为了优化set(list_2),解释器需要证明list_2(及其所有元素)在迭代之间不会改变.在一般情况下,这是一个困难的问题,如果口译员甚至不try 解决它,我也不会感到惊讶.

另一方面,集合文字不能在迭代之间更改其值,因此已知优化是安全的.

Python-3.x相关问答推荐

使用具有相同索引的多次出现的索引列表更新NumPy数组

动态范围内来自另外两列的列求和

我想判断df_entry_log[AM_PM],并根据测试填充列

文件名中的文件打开和撇号

使用 Fetch 提交表单到 Django 视图

在Pandas中,根据另一列中的重复值将数据分组为一列

将水平堆叠的数据排列成垂直

从列表的元素和python中的多个多索引数据帧执行方程

Python3:是否可以将变量用作函数调用的一部分

删除给定数组中所有元素为True的所有子数组

如何从形状汇总图中提取实际值

Python rolling_corr 取消后,应该用什么方法来处理

python 3:如何判断一个对象是否是一个函数?

TimescaleDB:是否可以从 Python 调用create_hypertable?

为什么不切换到 Python 3.x?

Python 错误:IndexError:字符串索引超出范围

sys.stdin.readline() 读取时没有提示,返回 'nothing in between'

Asyncio RuntimeError:事件循环已关闭

为什么排序列表比未排序列表大

将 Python 字节转换为无符号 8 位整数