合理的に一般的な操作はlist
、別のものに基づいて1 つをフィルタリングすることlist
です。人々はすぐに次のことに気付きます。
[x for x in list_1 if x in list_2]
大きな入力に対しては遅いです - それは O(n*m) です。うん。どうすればこれをスピードアップできますか? a を使用し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)]
あっ!Python は 1回だけでなくset(list_2)
毎回ビルドするため、これも O(n*m)です。
私はそれが話の終わりだと思った-pythonはそれを最適化してset
一度だけ構築することはできない。落とし穴に注意してください。それと一緒に暮らす必要があります。うーん。
#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)は集合リテラルを最適化できます。f()
おそらく aLOAD_GLOBAL
を aに置き換えるため、この場合よりもさらに高速ですLOAD_FAST
。
#python 2.7.5+
%timeit h()
10 loops, best of 3: 72.5 ms per loop
Python 2 は特にこの最適化を行いません。私はpython3が何をしているのかをさらに調査しようとしましたが、残念ながらdis.dis
理解表現の内部を調べることはできません. 基本的に興味深いものはすべて に変わりますMAKE_FUNCTION
。
だから今私は疑問に思っています-なぜpython 3.xはセットリテラルを最適化して1回だけビルドすることができset(list_2)
ますか?