Raisonnablement une opération commune est de filtrer une list
basé sur un autre list
. Les gens à trouver rapidement que cela:
[x for x in list_1 if x in list_2]
est lente pour les grandes entrées - il est O(n*m). Beurk. Comment pouvons-nous accélérer le processus? Utiliser un set
pour effectuer le filtrage des recherches O(1):
s = set(list_2)
[x for x in list_1 if x in s]
Cela donne ensemble de nice O(n) le comportement. J'ai cependant souvent voir même vétéran des codeurs de tomber dans Le Piège™:
[x for x in list_1 if x in set(list_2)]
Ack! C'est encore O(n*m) depuis le python s'appuie set(list_2)
tous les temps, et pas seulement une fois.
Je pensais que c'était la fin de l'histoire - python ne peut pas optimiser pour seulement de construire la set
une fois. Juste être conscient du piège. Dois vivre avec elle. Hmm.
#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
Hein, python (3.3) peut optimiser loin un ensemble de littéraux. C'est même plus rapide que l' f()
dans ce cas, sans doute parce qu'il arrive à remplacer un LOAD_GLOBAL
avec un LOAD_FAST
.
#python 2.7.5+
%timeit h()
10 loops, best of 3: 72.5 ms per loop
Python 2 notamment ne pas faire cette optimisation. J'ai essayé d'enquêter sur ce python3 est en train de faire, mais malheureusement, dis.dis
ne peut pas sonder les entrailles de la compréhension des expressions. En gros tout intéressant se transforme en MAKE_FUNCTION
.
Alors maintenant, je me demande - pourquoi python 3.x optimiser loin l'ensemble littérale construire seulement une fois, mais pas set(list_2)
?