J'ai l'impression de passer beaucoup de temps à écrire du code en Python, mais pas assez de temps pour créer du code Pythonic. Récemment, je suis tombé sur un drôle de petit problème que je pensais avoir une solution simple et idiomatique. Pour paraphraser l'original, je devais rassembler chaque paire séquentielle dans une liste. Par exemple, étant donné la liste [1,2,3,4,5,6]
, Je voulais calculer [(1,2),(3,4),(5,6)]
.
J'ai trouvé une solution rapide à l'époque qui ressemblait à du Java traduit. Revisitant la question, le mieux que j'ai pu faire était
l = [1,2,3,4,5,6]
[(l[2*x],l[2*x+1]) for x in range(len(l)/2)]
ce qui a pour effet secondaire de jeter le dernier numéro dans le cas où la longueur n'est pas égale.
Y a-t-il une approche plus idiomatique qui me manque, ou est-ce le meilleur que je vais obtenir?
Cela le fera un peu plus soigneusement:
>>> data = [1,2,3,4,5,6]
>>> Zip(data[0::2], data[1::2])
[(1, 2), (3, 4), (5, 6)]
(mais il est sans doute moins lisible si vous n'êtes pas familier avec la fonction "stride" des plages).
Comme votre code, il supprime la dernière valeur où vous avez un nombre impair de valeurs.
Celui souvent cité est:
Zip(*[iter(l)] * 2)
Je préfère cette version plus lisible de la solution iter
:
it = iter(l)
list(Zip(it, it))
# [(1, 2), (3, 4), (5, 6)]
Je copie généralement la recette grouper
de la documentation itertools dans mon code pour cela.
def grouper(n, iterable, fillvalue=None):
"grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"
args = [iter(iterable)] * n
return izip_longest(fillvalue=fillvalue, *args)
Que diriez-vous d'utiliser la fonction étape de range()
:
[(l[n],l[n+1]) for n in range(0,len(l),2)]
La bonne chose n'est probablement pas de calculer des listes, mais d'écrire une fonction itérateur-> itérateur. C'est plus générique - cela fonctionne sur tous les itérables, et si vous voulez le "figer" dans une liste, vous pouvez utiliser la fonction "list ()".
def groupElements(iterable, n):
# For your case, you can hardcode n=2, but I wanted the general case here.
# Also, you do not specify what to do if the
# length of the list is not divisible by 2
# I chose here to drop such elements
source = iter(iterable)
while True:
l = []
for i in range(n):
l.append(source.next())
yield Tuple(l)
Je suis surpris que le module itertools n'ait pas déjà de fonction pour cela - peut-être une future révision. D'ici là, n'hésitez pas à utiliser la version ci-dessus :)
essaye ça
def pairs(l, n):
return Zip(*[l[i::n] for i in range(n)])
Alors,
pairs([1, 2, 3, 4], 2)
donne
[(1, 2), (3, 4)]
toolz est une bibliothèque bien construite avec de nombreuses subtilités de programmation fonctionnelle négligées dans itertools. partition
résout ce problème (avec une option pour remplir la dernière entrée pour les listes de longueur impaire)
>>> list(toolz.partition(2, [1,2,3,4,5,6]))
[(1, 2), (3, 4), (5, 6)]
Si vous ne voulez pas perdre d'éléments si leur nombre dans la liste n'est même pas, essayez ceci:
>>> l = [1, 2, 3, 4, 5]
>>> [(l[i], l[i+1] if i+1 < len(l) else None) for i in range(0, len(l), 2)]
[(1, 2), (3, 4), (5, None)]