Assez souvent, j'ai trouvé la nécessité de traiter une liste par paires. Je me demandais quel serait le moyen pythonique et efficace de le faire, et j'ai trouvé ceci sur Google:
pairs = zip(t[::2], t[1::2])
Je pensais que c'était assez pythonique, mais après une discussion récente entre les idiomes et l'efficacité , j'ai décidé de faire quelques tests:
import time
from itertools import islice, izip
def pairs_1(t):
return zip(t[::2], t[1::2])
def pairs_2(t):
return izip(t[::2], t[1::2])
def pairs_3(t):
return izip(islice(t,None,None,2), islice(t,1,None,2))
A = range(10000)
B = xrange(len(A))
def pairs_4(t):
# ignore value of t!
t = B
return izip(islice(t,None,None,2), islice(t,1,None,2))
for f in pairs_1, pairs_2, pairs_3, pairs_4:
# time the pairing
s = time.time()
for i in range(1000):
p = f(A)
t1 = time.time() - s
# time using the pairs
s = time.time()
for i in range(1000):
p = f(A)
for a, b in p:
pass
t2 = time.time() - s
print t1, t2, t2-t1
Voici les résultats sur mon ordinateur:
1.48668909073 2.63187503815 1.14518594742
0.105381965637 1.35109519958 1.24571323395
0.00257992744446 1.46182489395 1.45924496651
0.00251388549805 1.70076990128 1.69825601578
Si je les interprète correctement, cela devrait signifier que la mise en œuvre de listes, l'indexation de listes et le découpage de listes en Python sont très efficaces. C'est un résultat à la fois réconfortant et inattendu.
Existe-t-il une autre «meilleure» façon de parcourir une liste par paires?
Notez que si la liste a un nombre impair d'éléments, le dernier ne sera dans aucune des paires.
Quelle serait la bonne façon de s'assurer que tous les éléments sont inclus?
J'ai ajouté ces deux suggestions à partir des réponses aux tests:
def pairwise(t):
it = iter(t)
return izip(it, it)
def chunkwise(t, size=2):
it = iter(t)
return izip(*[it]*size)
Voici les résultats:
0.00159502029419 1.25745987892 1.25586485863
0.00222492218018 1.23795199394 1.23572707176
Résultats à ce jour
Le plus pythonique et le plus efficace:
pairs = izip(t[::2], t[1::2])
Le plus efficace et le plus pythonique:
pairs = izip(*[iter(t)]*2)
Il m'a fallu un moment pour comprendre que la première réponse utilise deux itérateurs tandis que la seconde en utilise un seul.
Pour traiter des séquences avec un nombre impair d'éléments, la suggestion a été d'augmenter la séquence d'origine en ajoutant un élément ( None
) qui est associé au dernier élément précédent, ce qui peut être réalisé avec itertools.izip_longest()
.
finalement
Notez que, dans Python 3.x, zip()
se comporte comme itertools.izip()
, et itertools.izip()
est parti.
timeit
module.Réponses:
Ma façon préférée de le faire:
Lorsque vous souhaitez associer tous les éléments, vous pouvez évidemment avoir besoin d'une valeur de remplissage:
la source
itertools
section recettes.izip(*[iter(t)]*size)
Je dirais que votre solution initiale
pairs = zip(t[::2], t[1::2])
est la meilleure car elle est la plus facile à lire (et en Python 3,zip
renvoie automatiquement un itérateur au lieu d'une liste).Pour vous assurer que tous les éléments sont inclus, vous pouvez simplement étendre la liste de
None
.Ensuite, si la liste a un nombre impair d'éléments, la dernière paire le sera
(item, None)
.la source
Je commence par une petite clause de non-responsabilité - n'utilisez pas le code ci-dessous. Ce n'est pas du tout Pythonic, j'ai écrit juste pour m'amuser. C'est similaire à la
pairwise
fonction @ THC4k mais elle utiliseiter
et selambda
ferme. Il n'utilise pas deitertools
module et ne prend pas en chargefillvalue
. Je l'ai mis ici parce que quelqu'un pourrait le trouver intéressant:la source
En ce qui concerne la plupart des pythons, je dirais que les recettes fournies dans la documentation des sources python (dont certaines ressemblent beaucoup aux réponses fournies par @JochenRitzel) sont probablement votre meilleur pari;)
Sur python moderne, il vous suffit d'utiliser
zip_longest(*args, fillvalue=fillvalue)
selon la page doc correspondante .la source
Je ne peux pas le dire avec certitude mais j'en doute: toute autre traversée inclurait plus de code Python qui doit être interprété. Les fonctions intégrées comme zip () sont écrites en C, ce qui est beaucoup plus rapide.
Vérifiez la longueur de la liste et si c'est impair (
len(list) & 1 == 1
), copiez la liste et ajoutez un élément.la source
la source
Faites-le seulement:
la source
list(zip(l, l[1:]))
, et il ne divise pas la liste en paires.Voici un exemple de création de paires / jambes à l'aide d'un générateur. Les générateurs sont exempts de limites de pile
Exemple:
Production:
la source
[(0, 1), (2, 3), (4, 5)....
zip()
renvoie déjà un générateur en Python 3.x, @VladBezdenJuste au cas où quelqu'un aurait besoin de la réponse en termes d'algorithme, la voici:
Mais notez que votre liste d'origine sera également réduite à son dernier élément, car vous l'avez utilisée
pop
.la source