一个相当常见的操作是基于另一个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)
次?